LCOV - code coverage report
Current view: top level - cmdline - status.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 286 300 95.3 %
Date: 2025-10-28 11:59:11 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2013 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 "support.h"
      21             : #include "elem.h"
      22             : #include "state.h"
      23             : #include "parity.h"
      24             : #include "handle.h"
      25             : #include "raid/raid.h"
      26             : 
      27             : /****************************************************************************/
      28             : /* status */
      29             : 
      30          27 : unsigned day_ago(time_t ref, time_t now)
      31             : {
      32             :         /* in case some dates is in the future */
      33          27 :         if (now < ref)
      34           0 :                 return 0;
      35             : 
      36          27 :         return (now - ref) / (24 * 3600);
      37             : }
      38             : 
      39             : #define GRAPH_COLUMN 70
      40             : #define GRAPH_ROW 15
      41             : 
      42             : /**
      43             :  * Bit used to mark unscrubbed time info.
      44             :  */
      45             : #define TIME_NEW 1
      46             : 
      47          15 : int state_status(struct snapraid_state* state)
      48             : {
      49             :         block_off_t blockmax;
      50             :         block_off_t i;
      51             :         time_t* timemap;
      52             :         time_t now;
      53             :         block_off_t bad;
      54             :         block_off_t bad_first;
      55             :         block_off_t bad_last;
      56             :         block_off_t rehash;
      57             :         block_off_t count;
      58             :         unsigned l;
      59             :         unsigned dayoldest, daymedian, daynewest;
      60             :         unsigned bar_scrubbed[GRAPH_COLUMN];
      61             :         unsigned bar_new[GRAPH_COLUMN];
      62             :         unsigned barpos;
      63             :         unsigned barmax;
      64             :         time_t oldest, newest, median;
      65             :         unsigned x, y;
      66             :         tommy_node* node_disk;
      67             :         unsigned file_count;
      68             :         unsigned file_fragmented;
      69             :         unsigned extra_fragment;
      70             :         unsigned file_zerosubsecond;
      71             :         uint64_t file_size;
      72             :         uint64_t file_block_count;
      73             :         uint64_t file_block_free;
      74             :         block_off_t parity_block_free;
      75             :         unsigned unsynced_blocks;
      76             :         unsigned unscrubbed_blocks;
      77             :         uint64_t all_wasted;
      78             :         int free_not_zero;
      79             : 
      80             :         /* get the present time */
      81          15 :         now = time(0);
      82             : 
      83             :         /* keep track if at least a free info is available */
      84          15 :         free_not_zero = 0;
      85             : 
      86          15 :         blockmax = parity_allocated_size(state);
      87             : 
      88          15 :         log_tag("summary:block_size:%u\n", state->block_size);
      89          15 :         log_tag("summary:parity_block_count:%u\n", blockmax);
      90             : 
      91             :         /* get the minimum parity free space */
      92          15 :         parity_block_free = state->parity[0].free_blocks;
      93         105 :         for (l = 0; l < state->level; ++l) {
      94          90 :                 log_tag("summary:parity_block_total:%s:%u\n", lev_config_name(l), state->parity[l].total_blocks);
      95          90 :                 log_tag("summary:parity_block_free:%s:%u\n", lev_config_name(l), state->parity[l].free_blocks);
      96          90 :                 if (state->parity[l].free_blocks < parity_block_free)
      97           1 :                         parity_block_free = state->parity[l].free_blocks;
      98          90 :                 if (state->parity[l].free_blocks != 0)
      99          79 :                         free_not_zero = 1;
     100             :         }
     101          15 :         log_tag("summary:parity_block_free_min:%u\n", parity_block_free);
     102             : 
     103          15 :         printf("SnapRAID status report:\n");
     104          15 :         printf("\n");
     105          15 :         printf("   Files Fragmented Excess  Wasted  Used    Free  Use Name\n");
     106          15 :         printf("            Files  Fragments  GB      GB      GB\n");
     107             : 
     108             :         /* count fragments */
     109          15 :         file_count = 0;
     110          15 :         file_size = 0;
     111          15 :         file_block_count = 0;
     112          15 :         file_block_free = 0;
     113          15 :         file_fragmented = 0;
     114          15 :         extra_fragment = 0;
     115          15 :         file_zerosubsecond = 0;
     116          15 :         all_wasted = 0;
     117         105 :         for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) {
     118          90 :                 struct snapraid_disk* disk = node_disk->data;
     119             :                 tommy_node* node;
     120             :                 block_off_t j;
     121          90 :                 unsigned disk_file_count = 0;
     122          90 :                 unsigned disk_file_fragmented = 0;
     123          90 :                 unsigned disk_extra_fragment = 0;
     124          90 :                 unsigned disk_file_zerosubsecond = 0;
     125          90 :                 block_off_t disk_block_count = 0;
     126          90 :                 uint64_t disk_file_size = 0;
     127          90 :                 block_off_t disk_block_latest_used = 0;
     128             :                 block_off_t disk_block_max_by_space;
     129             :                 block_off_t disk_block_max_by_parity;
     130             :                 block_off_t disk_block_max;
     131             :                 int64_t wasted;
     132             : 
     133             :                 /* for each file in the disk */
     134          90 :                 node = disk->filelist;
     135       95624 :                 while (node) {
     136             :                         struct snapraid_file* file;
     137             : 
     138       95534 :                         file = node->data;
     139       95534 :                         node = node->next; /* next node */
     140             : 
     141       95534 :                         if (file->mtime_nsec == STAT_NSEC_INVALID
     142       95534 :                                 || file->mtime_nsec == 0
     143             :                         ) {
     144           0 :                                 ++file_zerosubsecond;
     145           0 :                                 ++disk_file_zerosubsecond;
     146           0 :                                 if (disk_file_zerosubsecond < 50)
     147           0 :                                         log_tag("zerosubsecond:%s:%s: \n", disk->name, file->sub);
     148           0 :                                 if (disk_file_zerosubsecond == 50)
     149           0 :                                         log_tag("zerosubsecond:%s:%s: (more follow)\n", disk->name, file->sub);
     150             :                         }
     151             : 
     152             :                         /* check fragmentation */
     153       95534 :                         if (file->blockmax != 0) {
     154             :                                 block_off_t prev_pos;
     155             :                                 block_off_t last_pos;
     156             :                                 int fragmented;
     157             : 
     158       95396 :                                 fragmented = 0;
     159       95396 :                                 prev_pos = fs_file2par_get(disk, file, 0);
     160      235735 :                                 for (j = 1; j < file->blockmax; ++j) {
     161      140339 :                                         block_off_t parity_pos = fs_file2par_get(disk, file, j);
     162      140339 :                                         if (prev_pos + 1 != parity_pos) {
     163         698 :                                                 fragmented = 1;
     164         698 :                                                 ++extra_fragment;
     165         698 :                                                 ++disk_extra_fragment;
     166             :                                         }
     167      140339 :                                         prev_pos = parity_pos;
     168             :                                 }
     169             : 
     170             :                                 /* keep track of latest block used */
     171       95396 :                                 last_pos = fs_file2par_get(disk, file, file->blockmax - 1);
     172       95396 :                                 if (last_pos > disk_block_latest_used) {
     173       92537 :                                         disk_block_latest_used = last_pos;
     174             :                                 }
     175             : 
     176       95396 :                                 if (fragmented) {
     177         582 :                                         ++file_fragmented;
     178         582 :                                         ++disk_file_fragmented;
     179             :                                 }
     180             : 
     181       95396 :                                 disk_block_count += file->blockmax;
     182             :                         }
     183             : 
     184             :                         /* count files */
     185       95534 :                         ++file_count;
     186       95534 :                         ++disk_file_count;
     187       95534 :                         file_size += file->size;
     188       95534 :                         file_block_count += file->blockmax;
     189       95534 :                         disk_file_size += file->size;
     190             :                 }
     191             : 
     192          90 :                 if (disk->free_blocks != 0)
     193          53 :                         free_not_zero = 1;
     194             : 
     195             :                 /* get the free block info */
     196          90 :                 disk_block_max_by_space = disk_block_count + disk->free_blocks;
     197          90 :                 disk_block_max_by_parity = blockmax + parity_block_free;
     198             : 
     199             :                 /* the maximum usable space in a disk is limited by the smallest */
     200             :                 /* of the disk size and the parity size */
     201             :                 /* the wasted space is the space that we have to leave */
     202             :                 /* free on the data disk, when the parity is filled up */
     203          90 :                 if (disk_block_max_by_space < disk_block_max_by_parity) {
     204          78 :                         disk_block_max = disk_block_max_by_space;
     205             :                 } else {
     206          12 :                         disk_block_max = disk_block_max_by_parity;
     207             :                 }
     208             : 
     209             :                 /* wasted space is the difference of the two maximum size */
     210             :                 /* if negative, it's extra space available in parity */
     211          90 :                 wasted = (int64_t)disk_block_max_by_space - (int64_t)disk_block_max_by_parity;
     212          90 :                 wasted *= state->block_size;
     213             : 
     214          90 :                 if (wasted > 0)
     215           6 :                         all_wasted += wasted;
     216          90 :                 file_block_free += disk_block_max - disk_block_count;
     217             : 
     218          90 :                 printf("%8u", disk_file_count);
     219          90 :                 printf("%8u", disk_file_fragmented);
     220          90 :                 printf("%8u", disk_extra_fragment);
     221          90 :                 if (wasted < -100LL * GIGA) {
     222          78 :                         printf("       -");
     223             :                 } else {
     224          12 :                         printf("%8.1f", (double)wasted / GIGA);
     225             :                 }
     226          90 :                 printf("%8" PRIu64, disk_file_size / GIGA);
     227             : 
     228          90 :                 if (disk_block_max == 0 && disk_block_count == 0) {
     229             :                         /* if the disk is empty and we don't have the free space info */
     230          37 :                         printf("       -");
     231          37 :                         printf("   - ");
     232             :                 } else {
     233          53 :                         printf("%8" PRIu64, (disk_block_max - disk_block_count) * (uint64_t)state->block_size / GIGA);
     234          53 :                         printf(" %3u%%", muldiv(disk_block_count, 100, disk_block_max));
     235             :                 }
     236          90 :                 printf(" %s\n", disk->name);
     237             : 
     238          90 :                 log_tag("summary:disk_file_count:%s:%u\n", disk->name, disk_file_count);
     239          90 :                 log_tag("summary:disk_block_count:%s:%u\n", disk->name, disk_block_count);
     240          90 :                 log_tag("summary:disk_fragmented_file_count:%s:%u\n", disk->name, disk_file_fragmented);
     241          90 :                 log_tag("summary:disk_excess_fragment_count:%s:%u\n", disk->name, disk_extra_fragment);
     242          90 :                 log_tag("summary:disk_zerosubsecond_file_count:%s:%u\n", disk->name, disk_file_zerosubsecond);
     243          90 :                 log_tag("summary:disk_file_size:%s:%" PRIu64 "\n", disk->name, disk_file_size);
     244          90 :                 log_tag("summary:disk_block_allocated:%s:%u\n", disk->name, disk_block_latest_used + 1);
     245          90 :                 log_tag("summary:disk_block_total:%s:%u\n", disk->name, disk->total_blocks);
     246          90 :                 log_tag("summary:disk_block_free:%s:%u\n", disk->name, disk->free_blocks);
     247          90 :                 log_tag("summary:disk_block_max_by_space:%s:%u\n", disk->name, disk_block_max_by_space);
     248          90 :                 log_tag("summary:disk_block_max_by_parity:%s:%u\n", disk->name, disk_block_max_by_parity);
     249          90 :                 log_tag("summary:disk_block_max:%s:%u\n", disk->name, disk_block_max);
     250          90 :                 log_tag("summary:disk_space_wasted:%s:%" PRId64 "\n", disk->name, wasted);
     251             :         }
     252             : 
     253             :         /* totals */
     254          15 :         printf(" --------------------------------------------------------------------------\n");
     255          15 :         printf("%8u", file_count);
     256          15 :         printf("%8u", file_fragmented);
     257          15 :         printf("%8u", extra_fragment);
     258          15 :         printf("%8.1f", (double)all_wasted / GIGA);
     259          15 :         printf("%8" PRIu64, file_size / GIGA);
     260          15 :         printf("%8" PRIu64, file_block_free * state->block_size / GIGA);
     261          15 :         printf(" %3u%%", muldiv(file_block_count, 100, file_block_count + file_block_free));
     262          15 :         printf("\n");
     263             : 
     264             :         /* warn about invalid data free info */
     265          15 :         if (!free_not_zero)
     266           1 :                 printf("\nWARNING! Free space info will be valid after the first sync.\n");
     267             : 
     268          15 :         log_tag("summary:file_count:%u\n", file_count);
     269          15 :         log_tag("summary:file_block_count:%" PRIu64 "\n", file_block_count);
     270          15 :         log_tag("summary:fragmented_file_count:%u\n", file_fragmented);
     271          15 :         log_tag("summary:excess_fragment_count:%u\n", extra_fragment);
     272          15 :         log_tag("summary:zerosubsecond_file_count:%u\n", file_zerosubsecond);
     273          15 :         log_tag("summary:file_size:%" PRIu64 "\n", file_size);
     274          15 :         log_tag("summary:parity_size:%" PRIu64 "\n", blockmax * (uint64_t)state->block_size);
     275          15 :         log_tag("summary:parity_size_max:%" PRIu64 "\n", (blockmax + parity_block_free) * (uint64_t)state->block_size);
     276          15 :         log_tag("summary:hash:%s\n", hash_config_name(state->hash));
     277          15 :         log_tag("summary:prev_hash:%s\n", hash_config_name(state->prevhash));
     278          15 :         log_tag("summary:best_hash:%s\n", hash_config_name(state->besthash));
     279          15 :         log_flush();
     280             : 
     281             :         /* copy the info a temp vector, and count bad/rehash/unsynced blocks */
     282          15 :         timemap = malloc_nofail(blockmax * sizeof(time_t));
     283          15 :         bad = 0;
     284          15 :         bad_first = 0;
     285          15 :         bad_last = 0;
     286          15 :         count = 0;
     287          15 :         rehash = 0;
     288          15 :         unsynced_blocks = 0;
     289          15 :         unscrubbed_blocks = 0;
     290          15 :         log_tag("block_count:%u\n", blockmax);
     291       39823 :         for (i = 0; i < blockmax; ++i) {
     292             :                 int one_invalid;
     293             :                 int one_valid;
     294             : 
     295       39808 :                 snapraid_info info = info_get(&state->infoarr, i);
     296             : 
     297             :                 /* for each disk */
     298       39808 :                 one_invalid = 0;
     299       39808 :                 one_valid = 0;
     300      278656 :                 for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) {
     301      238848 :                         struct snapraid_disk* disk = node_disk->data;
     302      238848 :                         struct snapraid_block* block = fs_par2block_find(disk, i);
     303             : 
     304      238848 :                         if (block_has_file(block))
     305      235735 :                                 one_valid = 1;
     306      238848 :                         if (block_has_invalid_parity(block))
     307          56 :                                 one_invalid = 1;
     308             :                 }
     309             : 
     310             :                 /* if both valid and invalid, we need to update */
     311       39808 :                 if (one_invalid && one_valid) {
     312          40 :                         ++unsynced_blocks;
     313             :                 }
     314             : 
     315             :                 /* skip unused blocks */
     316       39808 :                 if (info != 0) {
     317             :                         time_t scrub_time;
     318             : 
     319       39808 :                         if (info_get_bad(info)) {
     320        1772 :                                 if (bad == 0)
     321           2 :                                         bad_first = i;
     322        1772 :                                 bad_last = i;
     323        1772 :                                 ++bad;
     324             :                         }
     325             : 
     326       39808 :                         if (info_get_rehash(info))
     327        4607 :                                 ++rehash;
     328             : 
     329       39808 :                         scrub_time = info_get_time(info);
     330             : 
     331       39808 :                         if (info_get_justsynced(info)) {
     332       21356 :                                 ++unscrubbed_blocks;
     333             : 
     334             :                                 /* mark the time as not scrubbed */
     335       21356 :                                 scrub_time |= TIME_NEW;
     336             :                         }
     337             : 
     338       39808 :                         timemap[count++] = scrub_time;
     339             :                 }
     340             : 
     341       39808 :                 if (state->opt.gui) {
     342           0 :                         if (info != 0)
     343           0 :                                 log_tag("block:%u:%" PRIu64 ":%s:%s:%s:%s\n", i, (uint64_t)info_get_time(info), one_valid ? "used" : "", one_invalid ? "unsynced" : "", info_get_bad(info) ? "bad" : "", info_get_rehash(info) ? "rehash" : "");
     344             :                         else
     345           0 :                                 log_tag("block_noinfo:%u:%s:%s\n", i, one_valid ? "used" : "", one_invalid ? "unsynced" : "");
     346             :                 }
     347             :         }
     348             : 
     349          15 :         log_tag("summary:has_unsynced:%u\n", unsynced_blocks);
     350          15 :         log_tag("summary:has_unscrubbed:%u\n", unscrubbed_blocks);
     351          15 :         log_tag("summary:has_rehash:%u\n", rehash);
     352          15 :         log_tag("summary:has_bad:%u:%u:%u\n", bad, bad_first, bad_last);
     353          15 :         log_flush();
     354             : 
     355          15 :         if (!count) {
     356           6 :                 log_fatal("The array is empty.\n");
     357           6 :                 free(timemap);
     358           6 :                 return 0;
     359             :         }
     360             : 
     361             :         /* sort the info to get the time info */
     362           9 :         qsort(timemap, count, sizeof(time_t), time_compare);
     363             : 
     364             :         /* output the info map */
     365           9 :         i = 0;
     366           9 :         log_tag("info_count:%u\n", count);
     367          26 :         while (i < count) {
     368          17 :                 unsigned j = i + 1;
     369       39808 :                 while (j < count && timemap[i] == timemap[j])
     370       39791 :                         ++j;
     371          17 :                 if ((timemap[i] & TIME_NEW) == 0) {
     372           7 :                         log_tag("info_time:%" PRIu64 ":%u:scrubbed\n", (uint64_t)timemap[i], j - i);
     373             :                 } else {
     374          10 :                         log_tag("info_time:%" PRIu64 ":%u:new\n", (uint64_t)(timemap[i] & ~TIME_NEW), j - i);
     375             :                 }
     376          17 :                 i = j;
     377             :         }
     378             : 
     379           9 :         oldest = timemap[0];
     380           9 :         median = timemap[count / 2];
     381           9 :         newest = timemap[count - 1];
     382           9 :         dayoldest = day_ago(oldest, now);
     383           9 :         daymedian = day_ago(median, now);
     384           9 :         daynewest = day_ago(newest, now);
     385             : 
     386             :         /* compute graph limits */
     387           9 :         barpos = 0;
     388           9 :         barmax = 0;
     389         639 :         for (i = 0; i < GRAPH_COLUMN; ++i) {
     390             :                 time_t limit;
     391             :                 unsigned step_scrubbed, step_new;
     392             : 
     393         630 :                 limit = oldest + (newest - oldest) * (i + 1) / GRAPH_COLUMN;
     394             : 
     395         630 :                 step_scrubbed = 0;
     396         630 :                 step_new = 0;
     397       40438 :                 while (barpos < count && timemap[barpos] <= limit) {
     398       39808 :                         if ((timemap[barpos] & TIME_NEW) != 0)
     399       21356 :                                 ++step_new;
     400             :                         else
     401       18452 :                                 ++step_scrubbed;
     402       39808 :                         ++barpos;
     403             :                 }
     404             : 
     405         630 :                 if (step_new + step_scrubbed > barmax)
     406          12 :                         barmax = step_new + step_scrubbed;
     407             : 
     408         630 :                 bar_scrubbed[i] = step_scrubbed;
     409         630 :                 bar_new[i] = step_new;
     410             :         }
     411             : 
     412           9 :         printf("\n\n");
     413             : 
     414             :         /* print the graph */
     415         144 :         for (y = 0; y < GRAPH_ROW; ++y) {
     416         135 :                 if (y == 0)
     417           9 :                         printf("%3u%%|", barmax * 100 / count);
     418         126 :                 else if (y == GRAPH_ROW - 1)
     419           9 :                         printf("  0%%|");
     420         117 :                 else if (y == GRAPH_ROW / 2)
     421           9 :                         printf("%3u%%|", barmax * 50 / count);
     422             :                 else
     423         108 :                         printf("    |");
     424        9585 :                 for (x = 0; x < GRAPH_COLUMN; ++x) {
     425        9450 :                         unsigned pivot_upper = barmax * (GRAPH_ROW - y) / GRAPH_ROW;
     426        9450 :                         unsigned pivot_lower = barmax * (GRAPH_ROW - 1 - y) / GRAPH_ROW;
     427        9450 :                         unsigned both = bar_scrubbed[x] + bar_new[x];
     428        9450 :                         unsigned scrubbed = bar_scrubbed[x];
     429             : 
     430        9450 :                         if (both > pivot_upper) {
     431         156 :                                 if (scrubbed > pivot_lower)
     432          61 :                                         printf("*");
     433             :                                 else
     434          95 :                                         printf("o");
     435        9294 :                         } else if (both > pivot_lower) {
     436          16 :                                 if (scrubbed == both)
     437           6 :                                         printf("*");
     438             :                                 else
     439          10 :                                         printf("o");
     440             :                         } else {
     441        9278 :                                 if (y == GRAPH_ROW - 1)
     442         614 :                                         printf("_");
     443             :                                 else
     444        8664 :                                         printf(" ");
     445             :                         }
     446             :                 }
     447         135 :                 printf("\n");
     448             :         }
     449           9 :         printf("   %3u                    days ago of the last scrub/sync               %3u\n", dayoldest, daynewest);
     450             : 
     451           9 :         printf("\n");
     452             : 
     453           9 :         printf("The oldest block was scrubbed %u days ago, the median %u, the newest %u.\n", dayoldest, daymedian, daynewest);
     454             : 
     455           9 :         printf("\n");
     456             : 
     457           9 :         if (newest > now) {
     458           0 :                 printf("WARNING! You have scrub dates in the future! The next sync/scrub will truncate them!\n");
     459             :         }
     460             : 
     461           9 :         if (unsynced_blocks) {
     462           1 :                 printf("WARNING! The array is NOT fully synced.\n");
     463           1 :                 printf("You have a sync in progress at %u%%.\n", muldiv(blockmax - unsynced_blocks, 100, blockmax));
     464             :         } else {
     465           8 :                 printf("No sync is in progress.\n");
     466             :         }
     467             : 
     468           9 :         if (unscrubbed_blocks) {
     469           7 :                 printf("%u%% of the array is not scrubbed.\n", muldiv_upper(unscrubbed_blocks, 100, blockmax));
     470             :         } else {
     471           2 :                 printf("The full array was scrubbed at least one time.\n");
     472             :         }
     473             : 
     474           9 :         if (file_zerosubsecond) {
     475           0 :                 printf("You have %u files with a zero sub-second timestamp.\n", file_zerosubsecond);
     476           0 :                 printf("Run 'snapraid touch' to set their sub-second timestamps to a non-zero value.\n");
     477             :         } else {
     478           9 :                 printf("No file has a zero sub-second timestamp.\n");
     479             :         }
     480             : 
     481           9 :         if (rehash) {
     482           1 :                 printf("You have a rehash in progress at %u%%.\n", muldiv(count - rehash, 100, count));
     483             :         } else {
     484           8 :                 if (state->besthash != state->hash) {
     485           7 :                         printf("No rehash is in progress, but for optimal performance one is recommended.\n");
     486             :                 } else {
     487           1 :                         printf("No rehash is in progress or needed.\n");
     488             :                 }
     489             :         }
     490             : 
     491           9 :         if (bad) {
     492           2 :                 printf("DANGER! In the array there are %u errors!\n\n", bad);
     493             : 
     494           2 :                 if (bad_last - bad_first + 1 == bad) {
     495           0 :                         printf("They are from block %u to %u.\n", bad_first, bad_last);
     496             :                 } else {
     497             :                         block_off_t bad_range;
     498             :                         block_off_t bad_count;
     499             :                         block_off_t range_start;
     500             :                         block_off_t range_count;
     501             : 
     502           2 :                         printf("They are from block %u to %u, specifically at blocks:", bad_first, bad_last);
     503             : 
     504             :                         /* print some of the errors */
     505           2 :                         bad_range = 0;
     506           2 :                         bad_count = 0;
     507           2 :                         range_start = 0;
     508           2 :                         range_count = 0;
     509         885 :                         for (i = 0; i <= blockmax; ++i) { /* one extra iteration to print the final range */
     510         885 :                                 snapraid_info info = 0;
     511         885 :                                 int is_bad = 0;
     512             : 
     513         885 :                                 if (i < blockmax) {
     514         885 :                                         info = info_get(&state->infoarr, i);
     515         885 :                                         if (info != 0) /* unused blocks are never bad */
     516         885 :                                                 is_bad = info_get_bad(info);
     517             :                                 }
     518             : 
     519         885 :                                 if (is_bad) {
     520             :                                         /* create or extend the range */
     521         258 :                                         if (!range_count)
     522         202 :                                                 range_start = i;
     523         258 :                                         ++range_count;
     524             :                                 } else {
     525             :                                         /* break the range */
     526         627 :                                         if (range_count) {
     527         202 :                                                 if (range_count == 1) {
     528         157 :                                                         printf(" %u", range_start);
     529             :                                                 } else {
     530          45 :                                                         printf(" %u-%u", range_start, range_start + range_count - 1);
     531             :                                                 }
     532         202 :                                                 bad_count += range_count;
     533         202 :                                                 ++bad_range;
     534         202 :                                                 range_count = 0;
     535             :                                         }
     536             :                                 }
     537             : 
     538         885 :                                 if (bad_range > 100) {
     539           2 :                                         printf(" and %u more...", bad - bad_count);
     540           2 :                                         break;
     541             :                                 }
     542             :                         }
     543             : 
     544           2 :                         printf("\n");
     545             :                 }
     546             : 
     547           2 :                 printf("\n");
     548             : 
     549           2 :                 printf("To fix them use the command 'snapraid -e fix'.\n");
     550           2 :                 printf("The errors will disappear from the 'status' at the next 'scrub' command.\n");
     551             :         } else {
     552           7 :                 printf("No error detected.\n");
     553             :         }
     554             : 
     555             :         /* free the temp vector */
     556           9 :         free(timemap);
     557             : 
     558           9 :         return 0;
     559             : }
     560             : 

Generated by: LCOV version 1.0