LCOV - code coverage report
Current view: top level - cmdline - locate.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 119 126 94.4 %
Date: 2026-03-15 15:58:19 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2026 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 "parity.h"
      22             : #include "state.h"
      23             : #include "stream.h"
      24             : #include "locate.h"
      25             : 
      26             : struct snapraid_parity_entry {
      27             :         uint32_t block_size;
      28             :         block_off_t low; /**< Lower position in the parity */
      29             :         block_off_t high; /**< Higher position in the parity */
      30             :         block_off_t fragments; /**< Number of fragments in the parity */
      31             :         struct snapraid_file* file;
      32             :         struct snapraid_disk* disk;
      33             :         tommy_node node;
      34             : };
      35             : 
      36             : struct snapraid_locate_info {
      37             :         block_off_t block_max; /**< Number of blocks in the parity */
      38             :         data_off_t parity_size; /**< Size in bytes of the parity */
      39             :         block_off_t tail_block; /**< Number of blocks of the tail of the parity */
      40             :         block_off_t low_to_free_tail_block; /**< First block that has to be freed */
      41             : };
      42             : 
      43       61484 : static int parity_entry_compare(const void* void_a, const void* void_b)
      44             : {
      45       61484 :         const struct snapraid_parity_entry* entry_a = void_a;
      46       61484 :         const struct snapraid_parity_entry* entry_b = void_b;
      47             : 
      48       61484 :         if (entry_a->high < entry_b->high)
      49       36483 :                 return -1;
      50       25001 :         if (entry_a->high > entry_b->high)
      51       15466 :                 return 1;
      52             : 
      53        9535 :         if (entry_a->low < entry_b->low)
      54        3691 :                 return -1;
      55        5844 :         if (entry_a->low > entry_b->low)
      56        3170 :                 return 1;
      57             : 
      58        2674 :         return strcmp(entry_a->file->sub, entry_b->file->sub);
      59             : }
      60             : 
      61       11267 : static void dump_entry(void* void_entry)
      62             : {
      63             :         char esc_buffer[ESC_MAX];
      64       11267 :         struct snapraid_parity_entry* entry = void_entry;
      65       11267 :         printf("%12" PRIu64 " ", entry->low * (uint64_t)entry->block_size);
      66       11267 :         printf("%12" PRIu64 " ", (entry->high - entry->low + 1) * (uint64_t)entry->block_size);
      67       11267 :         printf("%8u ", entry->fragments);
      68       11267 :         printf("%s\n", fmt_term(entry->disk, entry->file->sub, esc_buffer));
      69       11267 : }
      70             : 
      71       11267 : static void add_size_to_sum(void* arg, void* void_entry)
      72             : {
      73       11267 :         struct snapraid_parity_entry* entry = void_entry;
      74       11267 :         data_off_t* size_sum = arg;
      75             : 
      76       11267 :         *size_sum += entry->file->size;
      77       11267 : }
      78             : 
      79       78921 : static void collect_parity_block_file(uint32_t block_size, struct snapraid_disk* disk, struct snapraid_file* file, tommy_list* file_list, block_off_t low_to_free_tail_block)
      80             : {
      81       78921 :         block_off_t parity_low = 0; /* lower block */
      82       78921 :         block_off_t parity_high = 0; /* higher block */
      83       78921 :         block_off_t parity_fragments = 0; /* number of fragments */
      84             : 
      85      276775 :         for (block_off_t i = 0; i < file->blockmax; ++i) {
      86      197854 :                 block_off_t parity_pos = fs_file2par_find(disk, file, i);
      87             : 
      88             :                 /* check if a valid position is found */
      89      197854 :                 if (parity_pos == POS_NULL) {
      90             :                         /* block not yet allocated */
      91           0 :                         continue;
      92             :                 }
      93             : 
      94      197854 :                 if (parity_fragments == 0) {
      95       78802 :                         parity_low = parity_pos;
      96       78802 :                         parity_high = parity_pos;
      97       78802 :                         parity_fragments = 1;
      98             :                 } else {
      99      119052 :                         if (parity_pos != parity_high + 1)
     100         241 :                                 ++parity_fragments;
     101      119052 :                         parity_high = parity_pos;
     102             :                 }
     103             :         }
     104             : 
     105       78921 :         if (parity_fragments == 0)
     106         119 :                 return; /* not allocated */
     107             : 
     108       78802 :         if (low_to_free_tail_block > 0 && parity_high < low_to_free_tail_block)
     109       56277 :                 return; /* entry not in the range to free */
     110             : 
     111             :         /* found a relevant block so add the corresponding file */
     112       22525 :         struct snapraid_parity_entry* entry = malloc_nofail(sizeof(struct snapraid_parity_entry));
     113       22525 :         entry->block_size = block_size;
     114       22525 :         entry->file = file;
     115       22525 :         entry->disk = disk;
     116       22525 :         entry->low = parity_low;
     117       22525 :         entry->high = parity_high;
     118       22525 :         entry->fragments = parity_fragments;
     119       22525 :         tommy_list_insert_tail(file_list, &entry->node, entry);
     120             : }
     121             : 
     122           5 : void state_locate_info(struct snapraid_state* state, uint64_t parity_tail, struct snapraid_locate_info* info)
     123             : {
     124           5 :         uint32_t block_size = state->block_size;
     125             : 
     126           5 :         info->block_max = parity_allocated_size(state);
     127           5 :         info->parity_size = info->block_max * (uint64_t)block_size;
     128             : 
     129           5 :         info->tail_block = (parity_tail + block_size - 1) / block_size;
     130             : 
     131           5 :         if (info->tail_block > info->block_max)
     132           0 :                 info->low_to_free_tail_block = 0;
     133             :         else
     134           5 :                 info->low_to_free_tail_block = info->block_max - info->tail_block;
     135           5 : }
     136             : 
     137           5 : void state_locate(struct snapraid_state* state, uint64_t parity_tail)
     138             : {
     139             :         char buf[64];
     140           5 :         uint32_t block_size = state->block_size;
     141             :         block_off_t low_to_free_tail_block;
     142             : 
     143           5 :         printf("SnapRAID locate report:\n");
     144           5 :         printf("\n");
     145             : 
     146           5 :         if (parity_tail == 0) {
     147           1 :                 printf("Locate all files\n\n");
     148           1 :                 low_to_free_tail_block = 0;
     149             :         } else {
     150           4 :                 printf("Locate files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
     151             : 
     152             :                 struct snapraid_locate_info info;
     153           4 :                 state_locate_info(state, parity_tail, &info);
     154             : 
     155           4 :                 printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
     156           4 :                 if (info.tail_block > info.block_max) {
     157           0 :                         printf("Specified tail greater than the parity size! Operate on the full parity\n");
     158             :                 }
     159             : 
     160           4 :                 low_to_free_tail_block = info.low_to_free_tail_block;
     161             :         }
     162             : 
     163           5 :         msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
     164             : 
     165             :         tommy_list files;
     166           5 :         tommy_list_init(&files);
     167          35 :         for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
     168          30 :                 struct snapraid_disk* disk = i->data;
     169       56402 :                 for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
     170       56372 :                         struct snapraid_file* file = j->data;
     171       56372 :                         collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
     172             :                 }
     173             :         }
     174             : 
     175           5 :         printf("\n");
     176             : 
     177           5 :         if (tommy_list_count(&files) == 0) {
     178           0 :                 printf("No files located in the specified parity tail.\n");
     179             :         } else {
     180           5 :                 tommy_list_sort(&files, parity_entry_compare);
     181             : 
     182           5 :                 data_off_t total_size_located = 0;
     183             : 
     184           5 :                 tommy_list_foreach_arg(&files, add_size_to_sum, &total_size_located);
     185             : 
     186           5 :                 printf("Located data in this range: %sB\n", fmt_size(total_size_located, buf, sizeof(buf)));
     187             : 
     188           5 :                 printf("\n");
     189             : 
     190             :                 /*      |<##################################################################72>|####80>| */
     191           5 :                 printf("       Offset         Span    Frags\n");
     192             : 
     193           5 :                 tommy_list_foreach(&files, dump_entry);
     194             :         }
     195             : 
     196           5 :         tommy_list_foreach(&files, free);
     197           5 : }
     198             : 
     199           2 : void state_locate_mark_tail_blocks_for_resync(struct snapraid_state* state, uint64_t parity_tail)
     200             : {
     201             :         char buf[64];
     202           2 :         uint32_t block_size = state->block_size;
     203             :         block_off_t low_to_free_tail_block;
     204             : 
     205           2 :         if (parity_tail == 0) {
     206           1 :                 printf("Forcing reallocation of all files\n\n");
     207           1 :                 low_to_free_tail_block = 0;
     208             :         } else {
     209           1 :                 msg_progress("Forcing reallocation of all files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
     210             : 
     211             :                 struct snapraid_locate_info info;
     212           1 :                 state_locate_info(state, parity_tail, &info);
     213             : 
     214           1 :                 printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
     215           1 :                 if (info.tail_block > info.block_max) {
     216           0 :                         printf("Specified tail greater than the parity size! Operate on the full parity\n");
     217             :                 }
     218             : 
     219           1 :                 low_to_free_tail_block = info.low_to_free_tail_block;
     220             :         }
     221             : 
     222           2 :         msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
     223             : 
     224             :         tommy_list files;
     225           2 :         tommy_list_init(&files);
     226          14 :         for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
     227          12 :                 struct snapraid_disk* disk = i->data;
     228       22561 :                 for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
     229       22549 :                         struct snapraid_file* file = j->data;
     230       22549 :                         collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
     231             :                 }
     232             :         }
     233             : 
     234           2 :         printf("\n");
     235             : 
     236           2 :         if (tommy_list_count(&files) == 0) {
     237           0 :                 printf("No files located in the specified parity tail.\n");
     238             :         } else {
     239             :                 /* process all the files partiall or fully overlapping the free zone */
     240       11260 :                 for (tommy_node* j = tommy_list_head(&files); j != 0; j = j->next) {
     241       11258 :                         struct snapraid_parity_entry* entry = j->data;
     242       11258 :                         struct snapraid_file* file = entry->file;
     243       11258 :                         struct snapraid_disk* disk = entry->disk;
     244             : 
     245             :                         /* reallocate the full file, not only the part of in the free zone */
     246             :                         /* this is required because the files blocks have to be in order in */
     247             :                         /* in the parity file. */
     248             :                         /* not reallocating the file head will prevent the reallocation of the file tail */
     249       40108 :                         for (block_off_t f = 0; f < file->blockmax; ++f) {
     250       28850 :                                 block_off_t parity_pos = fs_file2par_find(disk, file, f);
     251             : 
     252       28850 :                                 if (parity_pos == POS_NULL) {
     253             :                                         /* block not yet allocated */
     254           0 :                                         continue;
     255             :                                 }
     256             : 
     257       28850 :                                 struct snapraid_block* block = fs_file2block_get(file, f);
     258       28850 :                                 if (block_state_get(block) == BLOCK_STATE_BLK) {
     259             :                                         /* convert from BLK to REP */
     260       28850 :                                         block_state_set(block, BLOCK_STATE_REP);
     261             :                                 }
     262             :                         }
     263             :                 }
     264             :         }
     265             : 
     266           2 :         tommy_list_foreach(&files, free);
     267           2 : }
     268             : 

Generated by: LCOV version 1.0