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-04-29 15:04:44 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.0