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 :
|