Line data Source code
1 : // SPDX-License-Identifier: GPL-3.0-or-later
2 : // Copyright (C) 2011 Andrea Mazzoleni
3 :
4 : #include "portable.h"
5 :
6 : #include "support.h"
7 : #include "util.h"
8 : #include "elem.h"
9 : #include "import.h"
10 : #include "search.h"
11 : #include "state.h"
12 : #include "parity.h"
13 : #include "handle.h"
14 : #include "raid/raid.h"
15 : #include "raid/combo.h"
16 :
17 : /****************************************************************************/
18 : /* check */
19 :
20 779458 : static const char* es(int err)
21 : {
22 779458 : if (is_hw(err))
23 0 : return "error_io";
24 : else
25 779458 : return "error";
26 : }
27 :
28 : /**
29 : * A block that failed the hash check, or that was deleted.
30 : */
31 : struct failed_struct {
32 : /**
33 : * If we know for sure that the block is garbage or missing
34 : * and it needs to be recovered and rewritten to the disk.
35 : */
36 : int is_bad;
37 :
38 : /**
39 : * If what we have recovered may be not updated data,
40 : * an old version, or just garbage.
41 : *
42 : * Essentially, it means that we are not sure what we have recovered
43 : * is really correct. It's just our best guess.
44 : *
45 : * These "recovered" blocks are also written to the disk if the block is marked as ::is_bad.
46 : * But these files are marked also as FILE_IS_DAMAGED, and then renamed to .unrecoverable.
47 : *
48 : * Note that this could happen only for CHG blocks.
49 : */
50 : int is_outofdate;
51 :
52 : unsigned index; /**< Index of the failed block. */
53 : struct snapraid_block* block; /**< The failed block */
54 : struct snapraid_disk* disk; /**< The failed disk. */
55 : struct snapraid_file* file; /**< The failed file. 0 for DELETED block. */
56 : block_off_t file_pos; /**< Offset inside the file */
57 : struct snapraid_handle* handle; /**< The handle containing the failed block, or 0 for a DELETED block */
58 : };
59 :
60 : /**
61 : * Check if a block hash matches the specified buffer.
62 : * Return ==0 if equal
63 : */
64 511889 : static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_block* block, unsigned pos_size, unsigned char* buffer, unsigned char* buffer_zero)
65 : {
66 : unsigned char hash[HASH_MAX];
67 :
68 : /* now compute the hash of the valid part */
69 511889 : if (rehash) {
70 0 : memhash(state->prevhash, state->prevhashseed, hash, buffer, pos_size);
71 : } else {
72 511889 : memhash(state->hash, state->hashseed, hash, buffer, pos_size);
73 : }
74 :
75 : /* compare the hash */
76 511889 : if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
77 15681 : return -1;
78 : }
79 :
80 : /* compare to the end of the block */
81 496208 : if (pos_size < state->block_size) {
82 206294 : if (memcmp(buffer + pos_size, buffer_zero + pos_size, state->block_size - pos_size) != 0) {
83 54 : return -1;
84 : }
85 : }
86 :
87 496154 : return 0;
88 : }
89 :
90 : /**
91 : * Check if the hash of all the failed blocks we are expecting to recover are now matching.
92 : */
93 227459 : static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, void** buffer, void* buffer_zero)
94 : {
95 : unsigned j;
96 : int hash_checked;
97 :
98 227459 : hash_checked = 0; /* keep track if we check at least one block */
99 :
100 : /* check if the recovered blocks are OK */
101 728176 : for (j = 0; j < failed_count; ++j) {
102 : /* if we are expected to recover this block */
103 516452 : if (!failed[failed_map[j]].is_outofdate
104 : /* if the block has a hash to check */
105 511889 : && block_has_updated_hash(failed[failed_map[j]].block)
106 : ) {
107 : /* if a hash doesn't match, fail the check */
108 511889 : unsigned pos_size = file_block_size(failed[failed_map[j]].file, failed[failed_map[j]].file_pos, state->block_size);
109 511889 : if (blockcmp(state, rehash, failed[failed_map[j]].block, pos_size, buffer[failed[failed_map[j]].index], buffer_zero) != 0) {
110 15735 : log_tag("repair_hash_error:%u: Hash mismatch\n", failed_map[j]);
111 15735 : return 0;
112 : }
113 :
114 496154 : hash_checked = 1;
115 : }
116 : }
117 :
118 : /*
119 : * If nothing checked, we reject it
120 : * note that we are excluding this case at upper level
121 : * but checking again doesn't hurt
122 : */
123 211724 : if (!hash_checked) {
124 : /* LCOV_EXCL_START */
125 : return 0;
126 : /* LCOV_EXCL_STOP */
127 : }
128 :
129 : /*
130 : * If we checked something, and no block failed the check
131 : * recompute all the redundancy information
132 : */
133 211724 : raid_gen(diskmax, state->level, state->block_size, buffer);
134 211724 : return 1;
135 : }
136 :
137 : /**
138 : * Check if specified parity is now matching with a recomputed one.
139 : */
140 8 : static int is_parity_matching(struct snapraid_state* state, unsigned diskmax, unsigned i, void** buffer, void** buffer_recov)
141 : {
142 : /* recompute parity, note that we don't need parity over i */
143 8 : raid_gen(diskmax, i + 1, state->block_size, buffer);
144 :
145 : /* if the recovered parity block matches */
146 8 : if (memcmp(buffer[diskmax + i], buffer_recov[i], state->block_size) == 0) {
147 : /* recompute all the redundancy information */
148 8 : raid_gen(diskmax, state->level, state->block_size, buffer);
149 8 : return 1;
150 : }
151 :
152 0 : return 0;
153 : }
154 :
155 : /**
156 : * Repair errors.
157 : * Return <0 if failure for missing strategy, >0 if data is wrong and we cannot rebuild correctly, 0 on success.
158 : * If success, the parity is computed in the buffer variable.
159 : */
160 269628 : static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, void** buffer, void** buffer_recov, void* buffer_zero)
161 : {
162 : unsigned i, n;
163 : int error;
164 : int has_hash;
165 : int id[LEV_MAX];
166 : int ip[LEV_MAX];
167 :
168 : /* no fix required, already checked at higher level, but just to be sure */
169 269628 : if (failed_count == 0) {
170 : /* LCOV_EXCL_START */
171 : /* recompute only the parity */
172 : raid_gen(diskmax, state->level, state->block_size, buffer);
173 : return 0;
174 : /* LCOV_EXCL_STOP */
175 : }
176 :
177 269628 : n = state->level;
178 269628 : error = 0;
179 :
180 : /* setup vector of failed disk indexes */
181 950945 : for (i = 0; i < failed_count; ++i)
182 681317 : id[i] = failed[failed_map[i]].index;
183 :
184 : /*
185 : * Check if there is at least a failed block that can be checked for correctness using the hash
186 : * if there isn't, we have to sacrifice a parity block to check that the result is correct
187 : */
188 269628 : has_hash = 0;
189 950945 : for (i = 0; i < failed_count; ++i) {
190 : /* if we are expected to recover this block */
191 681317 : if (!failed[failed_map[i]].is_outofdate
192 : /* if the block has a hash to check */
193 672191 : && block_has_updated_hash(failed[failed_map[i]].block)
194 : )
195 672183 : has_hash = 1;
196 : }
197 :
198 : /*
199 : * If we don't have a hash, but we have an extra parity
200 : * (strictly-less failures than number of parities)
201 : */
202 269628 : if (!has_hash && failed_count < n) {
203 : /* number of parity to use, one more to check the recovering */
204 8 : unsigned r = failed_count + 1;
205 :
206 : /* all combinations (r of n) parities */
207 8 : combination_first(r, n, ip);
208 : do {
209 : /* if a parity is missing, do nothing */
210 24 : for (i = 0; i < r; ++i) {
211 16 : if (buffer_recov[ip[i]] == 0)
212 0 : break;
213 : }
214 8 : if (i != r)
215 0 : continue;
216 :
217 : /* copy the parities to use, one less because the last is used for checking */
218 16 : for (i = 0; i < r - 1; ++i)
219 8 : memcpy(buffer[diskmax + ip[i]], buffer_recov[ip[i]], state->block_size);
220 :
221 : /* recover using one less parity, the ip[r-1] one */
222 8 : raid_data(r - 1, id, ip, diskmax, state->block_size, buffer);
223 :
224 : /* use the remaining ip[r-1] parity to check the result */
225 8 : if (is_parity_matching(state, diskmax, ip[r - 1], buffer, buffer_recov))
226 8 : return 0;
227 :
228 : /* log */
229 0 : log_tag("recover_parity_error:%u:", pos);
230 0 : for (i = 0; i < r; ++i) {
231 0 : if (i != 0)
232 0 : log_tag(",");
233 0 : log_tag("%s", lev_config_name(ip[i]));
234 : }
235 0 : log_tag(": Parity mismatch\n");
236 0 : ++error;
237 0 : } while (combination_next(r, n, ip));
238 : }
239 :
240 : /*
241 : * If we have a hash, and enough parities
242 : * (less-or-equal failures than number of parities)
243 : */
244 269620 : if (has_hash && failed_count <= n) {
245 : /* number of parities to use equal to the number of failures */
246 222150 : unsigned r = failed_count;
247 :
248 : /* all combinations (r of n) parities */
249 222150 : combination_first(r, n, ip);
250 : do {
251 : /* if a parity is missing, do nothing */
252 748664 : for (i = 0; i < r; ++i) {
253 521205 : if (buffer_recov[ip[i]] == 0)
254 4753 : break;
255 : }
256 232212 : if (i != r)
257 4753 : continue;
258 :
259 : /* copy the parities to use */
260 743911 : for (i = 0; i < r; ++i)
261 516452 : memcpy(buffer[diskmax + ip[i]], buffer_recov[ip[i]], state->block_size);
262 :
263 : /* recover */
264 227459 : raid_data(r, id, ip, diskmax, state->block_size, buffer);
265 :
266 : /* use the hash to check the result */
267 227459 : if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
268 211724 : return 0;
269 :
270 : /* log */
271 15735 : log_tag("recover_hash_error:%u:", pos);
272 31470 : for (i = 0; i < r; ++i) {
273 15735 : if (i != 0)
274 0 : log_tag("/");
275 15735 : log_tag("%s", lev_config_name(ip[i]));
276 : }
277 15735 : log_tag(": Hash mismatch\n");
278 15735 : ++error;
279 40976 : } while (combination_next(r, n, ip));
280 : }
281 :
282 : /* return the number of failed attempts, or -1 if no strategy */
283 57896 : if (error)
284 10426 : return error;
285 :
286 47470 : log_tag("recover_strategy_error:%u: No strategy to recover from %u failures with %u parity %s hash\n",
287 : pos, failed_count, n, has_hash ? "with" : "without");
288 47470 : return -1;
289 : }
290 :
291 913667 : static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, void** buffer, void** buffer_recov, void* buffer_zero)
292 : {
293 : int ret;
294 : int error;
295 : unsigned j;
296 : int n;
297 : int something_to_recover;
298 : int something_unsynced;
299 : char esc_buffer[ESC_MAX];
300 :
301 913667 : error = 0;
302 :
303 : /* if nothing failed, just recompute the parity */
304 913667 : if (failed_count == 0) {
305 608377 : raid_gen(diskmax, state->level, state->block_size, buffer);
306 608377 : return 0;
307 : }
308 :
309 : /* logs the status */
310 1077440 : for (j = 0; j < failed_count; ++j) {
311 : const char* desc;
312 : const char* hash;
313 : const char* data;
314 772150 : struct snapraid_block* block = failed[j].block;
315 772150 : unsigned block_state = block_state_get(block);
316 :
317 772150 : switch (block_state) {
318 19434 : case BLOCK_STATE_DELETED : desc = "delete"; break;
319 16227 : case BLOCK_STATE_CHG : desc = "change"; break;
320 60632 : case BLOCK_STATE_REP : desc = "replace"; break;
321 675857 : case BLOCK_STATE_BLK : desc = "block"; break;
322 : /* LCOV_EXCL_START */
323 : default : desc = "unknown"; break;
324 : /* LCOV_EXCL_STOP */
325 : }
326 :
327 772150 : if (hash_is_invalid(block->hash)) {
328 0 : hash = "lost";
329 772150 : } else if (hash_is_zero(block->hash)) {
330 13175 : hash = "zero";
331 : } else {
332 758975 : hash = "known";
333 : }
334 :
335 772150 : if (failed[j].is_bad)
336 684349 : data = "bad";
337 : else
338 87801 : data = "good";
339 :
340 772150 : if (failed[j].file) {
341 752716 : struct snapraid_disk* disk = failed[j].disk;
342 752716 : struct snapraid_file* file = failed[j].file;
343 752716 : block_off_t file_pos = failed[j].file_pos;
344 :
345 752716 : log_tag("repair_entry:%u:%s:%s:%s:%s:%s:%u:\n", j, desc, hash, data, disk->name, esc_tag(file->sub, esc_buffer), file_pos);
346 : } else {
347 19434 : log_tag("repair_entry:%u:%s:%s:%s:\n", j, desc, hash, data);
348 : }
349 : }
350 :
351 : /*
352 : * Here we have to try two different strategies to recover, because in case the 'sync'
353 : * process is aborted, we don't know if the parity data is really updated just like after 'sync',
354 : * or if it still represents the state before the 'sync'.
355 : */
356 :
357 : /*
358 : * Note that if the 'sync' ends normally, we don't have any DELETED, REP and CHG blocks
359 : * and the two strategies are identical
360 : */
361 :
362 : /*
363 : * As first, we assume that the parity IS updated for the current state
364 : * and that we are going to recover the state after the last 'sync'.
365 : * In this case, parity contains info from BLK, REP and CHG blocks,
366 : * but not for DELETED.
367 : * We need to put in the recovering process only the bad blocks, because all the
368 : * others already contains the correct data read from disk, and the parity is correctly computed for them.
369 : * We are interested to recover BLK, REP and CHG blocks if they are marked as bad,
370 : * but we are not interested in DELETED ones.
371 : */
372 :
373 305290 : n = 0;
374 305290 : something_to_recover = 0; /* keep track if there is at least one block to fix */
375 1077440 : for (j = 0; j < failed_count; ++j) {
376 772150 : if (failed[j].is_bad) {
377 684349 : unsigned block_state = block_state_get(failed[j].block);
378 :
379 684349 : assert(block_state != BLOCK_STATE_DELETED); /* we cannot have bad DELETED blocks */
380 :
381 : /* if we have the hash for it */
382 684349 : if ((block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP)
383 : /* try to fetch the block using the known hash */
384 684341 : && (state_import_fetch(state, rehash, failed[j].block, buffer[failed[j].index]) == 0
385 679808 : || state_search_fetch(state, rehash, failed[j].file, failed[j].file_pos, failed[j].block, buffer[failed[j].index]) == 0)
386 : ) {
387 : /* we already have corrected it! */
388 22211 : log_tag("repair_hash_import:%u: Fixed by import\n", j);
389 : } else {
390 : /* otherwise try to recover it */
391 662138 : failed_map[n] = j;
392 662138 : ++n;
393 :
394 : /* we have something to try to recover */
395 662138 : something_to_recover = 1;
396 : }
397 : }
398 : }
399 :
400 : /* if nothing to fix */
401 305290 : if (!something_to_recover) {
402 45715 : log_tag("recover_sync:%u:%u: Skipped for already recovered\n", pos, n);
403 :
404 : /* recompute only the parity */
405 45715 : raid_gen(diskmax, state->level, state->block_size, buffer);
406 45715 : return 0;
407 : }
408 :
409 259575 : ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
410 259575 : if (ret == 0) {
411 : /*
412 : * Reprocess the CHG blocks, for which we don't have a hash to check
413 : * if they were BAD we have to use some heuristics to ensure that we have recovered
414 : * the state after the sync. If unsure, we assume the worst case
415 : */
416 :
417 723740 : for (j = 0; j < failed_count; ++j) {
418 : /* we take care only of BAD blocks we have to write back */
419 517498 : if (failed[j].is_bad) {
420 503594 : unsigned block_state = block_state_get(failed[j].block);
421 :
422 : /*
423 : * BLK and REP blocks are always OK, because at this point
424 : * we have already checked their hash
425 : */
426 503594 : if (block_state != BLOCK_STATE_CHG) {
427 503586 : assert(block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP);
428 503586 : continue;
429 : }
430 :
431 : /* for CHG blocks we have to 'guess' if they are correct or not */
432 :
433 : /*
434 : * If the hash is invalid we cannot check the result
435 : * this could happen if we have lost this information
436 : * after an aborted sync
437 : */
438 8 : if (hash_is_invalid(failed[j].block->hash)) {
439 : /* it may contain garbage */
440 0 : failed[j].is_outofdate = 1;
441 :
442 0 : log_tag("repair:hash_unknown:%u: Unknown hash\n", j);
443 8 : } else if (hash_is_zero(failed[j].block->hash)) {
444 : /*
445 : * If the block is not filled with 0, we are sure to have
446 : * restored it to the state after the 'sync'
447 : * instead, if the block is filled with 0, it could be either that the
448 : * block after the sync is really filled by 0, or that
449 : * we restored the block before the 'sync'.
450 : */
451 8 : if (memcmp(buffer[failed[j].index], buffer_zero, state->block_size) == 0) {
452 : /* it may contain garbage */
453 8 : failed[j].is_outofdate = 1;
454 :
455 8 : log_tag("repair_hash_unknown:%u: Maybe old zero\n", j);
456 : }
457 : } else {
458 : /*
459 : * If the hash is different than the previous one, we are sure to have
460 : * restored it to the state after the 'sync'
461 : * instead, if the hash matches, it could be either that the
462 : * block after the sync has this hash, or that
463 : * we restored the block before the 'sync'.
464 : */
465 0 : unsigned pos_size = file_block_size(failed[j].file, failed[j].file_pos, state->block_size);
466 0 : if (blockcmp(state, rehash, failed[j].block, pos_size, buffer[failed[j].index], buffer_zero) == 0) {
467 : /* it may contain garbage */
468 0 : failed[j].is_outofdate = 1;
469 :
470 0 : log_tag("repair_hash_unknown:%u: Maybe old data\n", j);
471 : }
472 : }
473 : }
474 : }
475 :
476 206242 : return 0;
477 : }
478 53333 : if (ret > 0)
479 10426 : error += ret;
480 :
481 53333 : if (ret < 0)
482 42907 : log_tag("recover_sync:%u:%u: Failed with no attempts\n", pos, n);
483 : else
484 10426 : log_tag("recover_sync:%u:%u: Failed with %d attempts\n", pos, n, ret);
485 :
486 : /*
487 : * Now assume that the parity IS NOT updated at the current state,
488 : * but still represent the state before the last 'sync' process.
489 : * In this case, parity contains info from BLK, REP (old version), CHG (old version) and DELETED blocks,
490 : * but not for REP (new version) and CHG (new version).
491 : * We are interested to recover BLK ones marked as bad,
492 : * but we are not interested to recover CHG (new version) and REP (new version) blocks,
493 : * even if marked as bad, because we don't have parity for them and it's just impossible,
494 : * and we are not interested to recover DELETED ones.
495 : */
496 53333 : n = 0;
497 53333 : something_to_recover = 0; /* keep track if there is at least one block to fix */
498 53333 : something_unsynced = 0; /* keep track if we have some unsynced info to process */
499 234852 : for (j = 0; j < failed_count; ++j) {
500 181519 : unsigned block_state = block_state_get(failed[j].block);
501 :
502 181519 : if (block_state == BLOCK_STATE_DELETED
503 172169 : || block_state == BLOCK_STATE_CHG
504 171466 : || block_state == BLOCK_STATE_REP
505 : ) {
506 : /*
507 : * If the block is CHG, REP or DELETED, we don't have the original content of block,
508 : * and we must try to recover it.
509 : * This applies to CHG and REP blocks even if they are not marked bad,
510 : * because the parity is computed with old content, and not with the new one.
511 : * Note that this recovering is done just to make it possible to recover any other BLK one,
512 : * we are not really interested in DELETED, CHG (old version) and REP (old version).
513 : */
514 10053 : something_unsynced = 1;
515 :
516 10053 : if (block_state == BLOCK_STATE_CHG
517 703 : && hash_is_zero(failed[j].block->hash)
518 : ) {
519 : /*
520 : * If the block was a ZERO block, restore it to the original 0 as before the 'sync'
521 : * We do this to just allow recovering of other BLK ones
522 : */
523 :
524 0 : memset(buffer[failed[j].index], 0, state->block_size);
525 : /*
526 : * Note that from now the buffer is definitely lost
527 : * we can do this only because it's the last retry of recovering
528 : */
529 :
530 : /* try to fetch the old block using the old hash for CHG and DELETED blocks */
531 10053 : } else if ((block_state == BLOCK_STATE_CHG || block_state == BLOCK_STATE_DELETED)
532 10053 : && hash_is_unique(failed[j].block->hash)
533 10053 : && state_import_fetch(state, rehash, failed[j].block, buffer[failed[j].index]) == 0) {
534 :
535 : /*
536 : * Note that from now the buffer is definitely lost
537 : * we can do this only because it's the last retry of recovering
538 : */
539 : } else {
540 : /* otherwise try to recover it */
541 9126 : failed_map[n] = j;
542 9126 : ++n;
543 :
544 : /*
545 : * Note that we don't set something_to_recover, because we are
546 : * not really interested to recover *only* old blocks.
547 : */
548 : }
549 :
550 : /*
551 : * Avoid using the hash of this block to verify the recovering
552 : * this applies to REP blocks because we are going to recover the old state
553 : * and the REP hash represents the new one
554 : * it also applies to CHG and DELETE blocks because we want to have
555 : * a successful recovering only if a BLK one is matching
556 : */
557 10053 : failed[j].is_outofdate = 1;
558 171466 : } else if (failed[j].is_bad) {
559 : /*
560 : * If the block is bad we don't know its content, and we try to recover it
561 : * At this point, we can have only BLK ones
562 : */
563 :
564 171466 : assert(block_state == BLOCK_STATE_BLK);
565 :
566 : /* we have something we are interested to recover */
567 171466 : something_to_recover = 1;
568 :
569 : /* we try to recover it */
570 171466 : failed_map[n] = j;
571 171466 : ++n;
572 : }
573 : }
574 :
575 : /*
576 : * If nothing to fix, we just don't try
577 : * if nothing unsynced we also don't retry, because it's the same try as before
578 : */
579 53333 : if (something_to_recover && something_unsynced) {
580 10053 : ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
581 10053 : if (ret == 0) {
582 : /*
583 : * Reprocess the REP and CHG blocks, for which we have recovered an old state
584 : * that we don't want to save into disk
585 : * we have already marked them, but we redo it for logging
586 : */
587 :
588 16470 : for (j = 0; j < failed_count; ++j) {
589 : /* we take care only of BAD blocks we have to write back */
590 10980 : if (failed[j].is_bad) {
591 5490 : unsigned block_state = block_state_get(failed[j].block);
592 :
593 5490 : if (block_state == BLOCK_STATE_CHG
594 5490 : || block_state == BLOCK_STATE_REP
595 : ) {
596 : /*
597 : * Mark that we have restored an old state
598 : * and we don't want to write it to the disk
599 : */
600 0 : failed[j].is_outofdate = 1;
601 :
602 0 : log_tag("repair_hash_unknown:%u: Surely old data\n", j);
603 : }
604 : }
605 : }
606 :
607 5490 : return 0;
608 : }
609 4563 : if (ret > 0)
610 0 : error += ret;
611 :
612 4563 : if (ret < 0)
613 4563 : log_tag("recover_unsync:%u:%u: Failed with no attempts\n", pos, n);
614 : else
615 0 : log_tag("recover_unsync:%u:%u: Failed with %d attempts\n", pos, n, ret);
616 : } else {
617 43280 : log_tag("recover_unsync:%u:%u: Skipped for%s%s\n", pos, n,
618 : !something_to_recover ? " nothing to recover" : "",
619 : !something_unsynced ? " nothing unsynced" : ""
620 : );
621 : }
622 :
623 : /* return the number of failed attempts, or -1 if no strategy */
624 47843 : if (error)
625 4936 : return error;
626 : else
627 42907 : return -1;
628 : }
629 :
630 : /**
631 : * Post process all the files at the specified block index ::i.
632 : * For each file, if we are at the last block, close it,
633 : * adjust the timestamp, and print the result.
634 : *
635 : * This works only if the whole file is processed, including its last block.
636 : * This doesn't always happen, like with an explicit end block.
637 : *
638 : * In such case, the check/fix command won't report any information of the
639 : * files partially checked.
640 : */
641 953182 : static int file_post(struct snapraid_state* state, int fix, unsigned i, struct snapraid_handle* handle, unsigned diskmax)
642 : {
643 : unsigned j;
644 : int ret;
645 : char esc_buffer[ESC_MAX];
646 : char esc_buffer_alt[ESC_MAX];
647 :
648 : /*
649 : * For all the files print the final status, and do the final time fix
650 : * we also ensure to close files after processing the last block
651 : */
652 6643786 : for (j = 0; j < diskmax; ++j) {
653 : struct snapraid_block* block;
654 : struct snapraid_disk* disk;
655 : struct snapraid_file* collide_file;
656 : struct snapraid_file* file;
657 : block_off_t file_pos;
658 : uint64_t inode;
659 :
660 5690604 : disk = handle[j].disk;
661 5690604 : if (!disk) {
662 : /* if no disk, nothing to do */
663 3471117 : continue;
664 : }
665 :
666 5676543 : block = fs_par2block_find(disk, i);
667 5676543 : if (!block_has_file(block)) {
668 : /* if no file, nothing to do */
669 241947 : continue;
670 : }
671 :
672 5434596 : file = fs_par2file_get(disk, i, &file_pos);
673 :
674 : /* if it isn't the last block in the file */
675 5434596 : if (!file_block_is_last(file, file_pos)) {
676 : /* nothing to do */
677 3215109 : continue;
678 : }
679 :
680 : /* if the file is excluded, we have nothing to adjust as the file is never written */
681 2219487 : if (file_flag_has(file, FILE_IS_EXCLUDED)
682 2136675 : || (state->opt.syncedonly && file_flag_has(file, FILE_IS_UNSYNCED))) {
683 : /* nothing to do, but close the file */
684 82812 : goto close_and_continue;
685 : }
686 :
687 : /* finish the fix process if it's the last block of the files */
688 2136675 : if (fix) {
689 : /*
690 : * Mark that we finished with this file
691 : * to identify later any NOT finished ones
692 : */
693 784484 : file_flag_set(file, FILE_IS_FINISHED);
694 :
695 : /* if the file is damaged, meaning that a fix failed */
696 784484 : if (file_flag_has(file, FILE_IS_DAMAGED)) {
697 : /* rename it to .unrecoverable */
698 : char path[PATH_MAX];
699 : char path_to[PATH_MAX];
700 :
701 62987 : pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
702 62987 : pathprint(path_to, sizeof(path_to), "%s%s.unrecoverable", disk->dir, file->sub);
703 :
704 : /* ensure to close the file before renaming */
705 62987 : if (handle[j].file == file) {
706 62987 : ret = handle_close(&handle[j]);
707 62987 : if (ret != 0) {
708 : /* LCOV_EXCL_START */
709 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
710 : log_fatal_errno(errno, disk->name);
711 : return -1;
712 : /* LCOV_EXCL_STOP */
713 : }
714 : }
715 :
716 62987 : ret = rename(path, path_to);
717 62987 : if (ret != 0) {
718 : /* LCOV_EXCL_START */
719 : log_fatal(errno, "Error renaming '%s%s'. %s.\n", disk->dir, file->sub, strerror(errno));
720 : log_tag("%s:%u:%s:%s: Rename error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
721 : log_fatal_errno(errno, disk->name);
722 : return -1;
723 : /* LCOV_EXCL_STOP */
724 : }
725 :
726 62987 : log_tag("status:unrecoverable:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
727 62987 : msg_info("unrecoverable %s\n", fmt_term(disk, file->sub, esc_buffer));
728 :
729 : /* and do not set the time if damaged */
730 62987 : goto close_and_continue;
731 : }
732 :
733 : /* if the file is not fixed, meaning that it is untouched */
734 721497 : if (!file_flag_has(file, FILE_IS_FIXED)) {
735 : /* nothing to do, but close the file */
736 589540 : goto close_and_continue;
737 : }
738 :
739 : /*
740 : * If the file is closed or different than the one expected, reopen it
741 : * a different open file could happen when filtering for bad blocks
742 : */
743 131957 : if (handle[j].file != file) {
744 : /* keep a pointer at the file we are going to close for error reporting */
745 0 : struct snapraid_file* report = handle[j].file;
746 0 : ret = handle_close(&handle[j]);
747 0 : if (ret != 0) {
748 : /* LCOV_EXCL_START */
749 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), i, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
750 : log_fatal_errno(errno, disk->name);
751 : return -1;
752 : /* LCOV_EXCL_STOP */
753 : }
754 :
755 : /*
756 : * Reopen it as readonly, as to set the mtime readonly access it's enough
757 : * we know that the file exists because it has the FILE_IS_FIXED tag
758 : */
759 0 : ret = handle_open(&handle[j], file, state->file_mode, log_error, log_error); /* output a message for missing files */
760 0 : if (ret != 0) {
761 : /* LCOV_EXCL_START */
762 : log_tag("%s:%u:%s:%s: Open error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
763 : log_fatal_errno(errno, disk->name);
764 : return -1;
765 : /* LCOV_EXCL_STOP */
766 : }
767 : }
768 :
769 131957 : log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
770 131957 : msg_info("recovered %s\n", fmt_term(disk, file->sub, esc_buffer));
771 :
772 131957 : inode = handle[j].st.st_ino;
773 :
774 : /* search for the corresponding inode */
775 131957 : collide_file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode));
776 :
777 : /*
778 : * If the inode is already in the database and it refers to a different file name,
779 : * we can fix the file time ONLY if the time and size allow to differentiate
780 : * between the two files
781 : *
782 : * For example, suppose we delete a bunch of files with all the same size and time,
783 : * when recreating them the inodes may be reused in a different order,
784 : * and at the next sync some files may have matching inode/size/time even if different name
785 : * not allowing sync to detect that the file is changed and not renamed
786 : */
787 131957 : if (!collide_file /* if not in the database, there is no collision */
788 37370 : || strcmp(collide_file->sub, file->sub) == 0 /* if the name is the same, it's the right collision */
789 27768 : || collide_file->size != file->size /* if the size is different, the collision is identified */
790 14 : || collide_file->mtime_sec != file->mtime_sec /* if the mtime is different, the collision is identified */
791 9 : || collide_file->mtime_nsec != file->mtime_nsec /* same for mtime_nsec */
792 : ) {
793 : /* set the original modification time */
794 131956 : ret = handle_utime(&handle[j]);
795 131956 : if (ret == -1) {
796 : /* LCOV_EXCL_START */
797 : log_tag("%s:%u:%s:%s: Time error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
798 : log_fatal_errno(errno, disk->name);
799 :
800 : /* mark the file as damaged */
801 : file_flag_set(file, FILE_IS_DAMAGED);
802 : return -1;
803 : /* LCOV_EXCL_STOP */
804 : }
805 : } else {
806 1 : log_tag("collision:%s:%s:%s: Not setting modification time to avoid inode collision\n", disk->name, esc_tag(file->sub, esc_buffer), esc_tag(collide_file->sub, esc_buffer_alt));
807 : }
808 : } else {
809 : /*
810 : * We are not fixing, but only checking
811 : * print just the final status
812 : */
813 1352191 : if (file_flag_has(file, FILE_IS_DAMAGED)) {
814 9595 : log_tag("status:unrecoverable:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
815 9595 : msg_info("unrecoverable %s\n", fmt_term(disk, file->sub, esc_buffer));
816 1342596 : } else if (file_flag_has(file, FILE_IS_FIXED)) {
817 84208 : log_tag("status:recoverable:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
818 84208 : msg_info("recoverable %s\n", fmt_term(disk, file->sub, esc_buffer));
819 : } else {
820 : /* we don't use msg_verbose() because it also goes into the log */
821 1258388 : if (msg_level >= MSG_VERBOSE) {
822 23147 : log_tag("status:correct:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
823 23147 : msg_info("correct %s\n", fmt_term(disk, file->sub, esc_buffer));
824 : }
825 : }
826 : }
827 :
828 1235241 : close_and_continue:
829 : /*
830 : * If the opened file is the correct one, close it
831 : * in case of excluded and fragmented files it's possible
832 : * that the opened file is not the current one
833 : */
834 2219487 : if (handle[j].file == file) {
835 : /*
836 : * Ensure to close the file just after finishing with it
837 : * to avoid keeping it open without any possible use
838 : */
839 2069882 : ret = handle_close(&handle[j]);
840 2069882 : if (ret != 0) {
841 : /* LCOV_EXCL_START */
842 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
843 : log_fatal_errno(errno, disk->name);
844 : return -1;
845 : /* LCOV_EXCL_STOP */
846 : }
847 : }
848 : }
849 :
850 953182 : return 0;
851 : }
852 :
853 : /**
854 : * Check if we have to process the specified block index ::i.
855 : */
856 975899 : static int block_is_enabled(struct snapraid_state* state, block_off_t i, struct snapraid_handle* handle, unsigned diskmax)
857 : {
858 : unsigned j;
859 : unsigned l;
860 :
861 : /* filter for bad blocks */
862 975899 : if (state->opt.badblockonly) {
863 : snapraid_info info;
864 :
865 : /* get block specific info */
866 0 : info = info_get(&state->infoarr, i);
867 :
868 : /*
869 : * Filter specifically only for bad blocks
870 : */
871 0 : return info_get_bad(info);
872 : }
873 :
874 : /* filter for the parity */
875 975899 : if (state->opt.badfileonly) {
876 : snapraid_info info;
877 :
878 : /* get block specific info */
879 9374 : info = info_get(&state->infoarr, i);
880 :
881 : /*
882 : * If the block is bad, it has to be processed
883 : *
884 : * This is not necessary in normal cases because if a block is bad,
885 : * it necessary needs to have a file related to it, and files with
886 : * bad blocks are fully included.
887 : *
888 : * But some files may be excluded by additional filter options,
889 : * so it's not always true, and this ensures to always check all
890 : * the bad blocks.
891 : */
892 9374 : if (info_get_bad(info))
893 1772 : return 1;
894 : } else {
895 : /* if a parity is not excluded, include all blocks, even unused ones */
896 1120961 : for (l = 0; l < state->level; ++l) {
897 1074281 : if (!state->parity[l].is_excluded_by_filter) {
898 919845 : return 1;
899 : }
900 : }
901 : }
902 :
903 : /* filter for the files */
904 240006 : for (j = 0; j < diskmax; ++j) {
905 : struct snapraid_block* block;
906 :
907 : /* if no disk, nothing to check */
908 217289 : if (!handle[j].disk)
909 0 : continue;
910 :
911 217289 : block = fs_par2block_find(handle[j].disk, i);
912 :
913 : /*
914 : * Try to recover all files, even the ones without hash
915 : * because in some cases we can recover also them
916 : */
917 217289 : if (block_has_file(block)) {
918 210825 : struct snapraid_file* file = fs_par2file_get(handle[j].disk, i, 0);
919 210825 : if (!file_flag_has(file, FILE_IS_EXCLUDED)) { /* only if the file is not filtered out */
920 31565 : return 1;
921 : }
922 : }
923 : }
924 :
925 22717 : return 0;
926 : }
927 :
928 132 : static int state_check_process(struct snapraid_state* state, int fix, struct snapraid_parity_handle** parity, block_off_t blockstart, block_off_t blockmax)
929 : {
930 : struct snapraid_handle* handle;
931 : unsigned diskmax;
932 : block_off_t i;
933 : unsigned j;
934 : void* buffer_alloc;
935 : void** buffer;
936 : unsigned buffermax;
937 : int ret;
938 : data_off_t countsize;
939 : block_off_t countpos;
940 : block_off_t countmax;
941 : unsigned soft_error;
942 : unsigned io_error;
943 : unsigned silent_error;
944 : unsigned unrecoverable_error;
945 : unsigned recovered_error;
946 : struct failed_struct* failed;
947 : unsigned* failed_map;
948 : unsigned l;
949 : char esc_buffer[ESC_MAX];
950 : char esc_buffer_alt[ESC_MAX];
951 : bit_vect_t* block_enabled;
952 : struct snapraid_bw bw;
953 :
954 132 : handle = handle_mapping(state, &diskmax);
955 :
956 : /* initialize the bandwidth context */
957 132 : bw_init(&bw, state->opt.bwlimit);
958 :
959 : /* share the bandwidth context with all handles */
960 894 : for (j = 0; j < diskmax; ++j)
961 762 : handle[j].bw = &bw;
962 626 : for (j = 0; j < state->level; ++j)
963 494 : if (parity[j])
964 476 : parity[j]->bw = &bw;
965 :
966 : /* we need 1 * data + 2 * parity + 1 * zero */
967 132 : buffermax = diskmax + 2 * state->level + 1;
968 :
969 132 : buffer = malloc_nofail_vector_align(diskmax, buffermax, state->block_size, &buffer_alloc);
970 132 : if (!state->opt.skip_self)
971 0 : mtest_vector(buffermax, state->block_size, buffer);
972 :
973 : /* fill up the zero buffer */
974 132 : memset(buffer[buffermax - 1], 0, state->block_size);
975 132 : raid_zero(buffer[buffermax - 1]);
976 :
977 132 : failed = malloc_nofail(diskmax * sizeof(struct failed_struct));
978 132 : failed_map = malloc_nofail(diskmax * sizeof(unsigned));
979 :
980 132 : soft_error = 0;
981 132 : io_error = 0;
982 132 : silent_error = 0;
983 132 : unrecoverable_error = 0;
984 132 : recovered_error = 0;
985 :
986 132 : msg_progress("Selecting...\n");
987 :
988 : /* first count the number of blocks to process */
989 132 : countmax = 0;
990 132 : block_enabled = calloc_nofail(1, bit_vect_size(blockmax)); /* preinitialize to 0 */
991 976031 : for (i = blockstart; i < blockmax; ++i) {
992 975899 : if (!block_is_enabled(state, i, handle, diskmax))
993 22717 : continue;
994 953182 : bit_vect_set(block_enabled, i);
995 953182 : ++countmax;
996 : }
997 :
998 132 : if (fix)
999 51 : msg_progress("Fixing...\n");
1000 81 : else if (!state->opt.auditonly)
1001 76 : msg_progress("Checking...\n");
1002 : else
1003 5 : msg_progress("Hashing...\n");
1004 :
1005 : /* check all the blocks in files */
1006 132 : countsize = 0;
1007 132 : countpos = 0;
1008 :
1009 132 : int alert = state_progress_begin(state, blockstart, blockmax, countmax);
1010 132 : if (alert > 0)
1011 0 : goto end;
1012 132 : if (alert < 0)
1013 0 : goto bail;
1014 :
1015 976031 : for (i = blockstart; i < blockmax; ++i) {
1016 : unsigned failed_count;
1017 : int valid_parity;
1018 : int used_parity;
1019 : snapraid_info info;
1020 : int rehash;
1021 :
1022 975899 : if (!bit_vect_test(block_enabled, i)) {
1023 : /* continue with the next block */
1024 22717 : continue;
1025 : }
1026 :
1027 : /*
1028 : * If we have valid parity, and it makes sense to check its content.
1029 : * If we already know that the parity is invalid, we just read the file
1030 : * but we don't report parity errors
1031 : * Note that with auditonly, we anyway skip the full parity check,
1032 : * because we also don't read it at all
1033 : */
1034 953182 : valid_parity = 1;
1035 :
1036 : /* If the parity is used by at least one file */
1037 953182 : used_parity = 0;
1038 :
1039 : /* keep track of the number of failed blocks */
1040 953182 : failed_count = 0;
1041 :
1042 : /* get block specific info */
1043 953182 : info = info_get(&state->infoarr, i);
1044 :
1045 : /* if we have to use the old hash */
1046 953182 : rehash = info_get_rehash(info);
1047 :
1048 : /* for each disk, process the block */
1049 6643786 : for (j = 0; j < diskmax; ++j) {
1050 : int read_size;
1051 : unsigned char hash[HASH_MAX];
1052 : struct snapraid_disk* disk;
1053 : struct snapraid_block* block;
1054 : struct snapraid_file* file;
1055 : block_off_t file_pos;
1056 : unsigned block_state;
1057 :
1058 : /* if the disk position is not used */
1059 5690604 : disk = handle[j].disk;
1060 5690604 : if (!disk) {
1061 : /* use an empty block */
1062 14061 : memset(buffer[j], 0, state->block_size);
1063 1014777 : continue;
1064 : }
1065 :
1066 : /* if the disk block is not used */
1067 5676543 : block = fs_par2block_find(disk, i);
1068 5676543 : if (block == BLOCK_NULL) {
1069 : /* use an empty block */
1070 222513 : memset(buffer[j], 0, state->block_size);
1071 222513 : continue;
1072 : }
1073 :
1074 : /* get the state of the block */
1075 5454030 : block_state = block_state_get(block);
1076 :
1077 : /* if the parity is not valid */
1078 5454030 : if (block_has_invalid_parity(block)) {
1079 : /*
1080 : * Mark the parity as invalid, and don't try to check/fix it
1081 : * because it will be recomputed at the next sync
1082 : */
1083 96293 : valid_parity = 0;
1084 : /* follow */
1085 : }
1086 :
1087 : /* if the block is DELETED */
1088 5454030 : if (block_state == BLOCK_STATE_DELETED) {
1089 : /* use an empty block */
1090 19434 : memset(buffer[j], 0, state->block_size);
1091 :
1092 : /*
1093 : * Store it in the failed set, because potentially
1094 : * the parity may be still computed with the previous content
1095 : */
1096 19434 : failed[failed_count].is_bad = 0; /* note that is_bad==0 <=> file==0 */
1097 19434 : failed[failed_count].is_outofdate = 0;
1098 19434 : failed[failed_count].index = j;
1099 19434 : failed[failed_count].block = block;
1100 19434 : failed[failed_count].disk = disk;
1101 19434 : failed[failed_count].file = 0;
1102 19434 : failed[failed_count].file_pos = 0;
1103 19434 : failed[failed_count].handle = 0;
1104 19434 : ++failed_count;
1105 19434 : continue;
1106 : }
1107 :
1108 : /* here we are sure that the parity is used by a file */
1109 5434596 : used_parity = 1;
1110 :
1111 : /* get the file of this block */
1112 5434596 : file = fs_par2file_get(disk, i, &file_pos);
1113 :
1114 : /* if we are only hashing, we can skip excluded files and don't even read them */
1115 5434596 : if (state->opt.auditonly && file_flag_has(file, FILE_IS_EXCLUDED)) {
1116 : /*
1117 : * Use an empty block
1118 : * in true, this is unnecessary, because we are not checking any parity
1119 : * but we keep it for completeness
1120 : */
1121 0 : memset(buffer[j], 0, state->block_size);
1122 0 : continue;
1123 : }
1124 :
1125 : /* if the file is closed or different than the current one */
1126 5434596 : if (handle[j].file == 0 || handle[j].file != file) {
1127 : /* keep a pointer at the file we are going to close for error reporting */
1128 2363483 : struct snapraid_file* report = handle[j].file;
1129 2363483 : ret = handle_close(&handle[j]);
1130 2363483 : if (ret == -1) {
1131 : /* LCOV_EXCL_START */
1132 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), i, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
1133 : log_fatal_errno(errno, disk->name);
1134 : log_fatal(errno, "Stopping at block %u\n", i);
1135 :
1136 : ++unrecoverable_error;
1137 : goto bail;
1138 : /* LCOV_EXCL_STOP */
1139 : }
1140 :
1141 : /* if fixing, and the file is not excluded, we must open for writing */
1142 2363483 : if (fix && !file_flag_has(file, FILE_IS_EXCLUDED)) {
1143 : /* if fixing, create the file, open for writing and resize if required */
1144 790591 : ret = handle_create(&handle[j], file, state->file_mode);
1145 790591 : if (ret == -1) {
1146 : /* LCOV_EXCL_START */
1147 : log_tag("%s:%u:%s:%s: Create error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
1148 : log_fatal_errno(errno, disk->name);
1149 : log_fatal(errno, "Stopping at block %u\n", i);
1150 :
1151 : ++unrecoverable_error;
1152 : goto bail;
1153 : /* LCOV_EXCL_STOP */
1154 : }
1155 :
1156 : /* check if the file was just created */
1157 790591 : if (handle[j].created != 0) {
1158 : /*
1159 : * If fragmented, it may be reopened, so remember that the file
1160 : * was originally missing
1161 : */
1162 123783 : file_flag_set(file, FILE_IS_CREATED);
1163 : }
1164 : } else {
1165 : /* open the file only for reading */
1166 1572892 : if (!file_flag_has(file, FILE_IS_MISSING)) {
1167 1445287 : ret = handle_open(&handle[j], file, state->file_mode, log_error, state->opt.expected_missing ? log_expected : 0);
1168 : } else {
1169 127605 : errno = ENOENT;
1170 127605 : ret = -1; /* if the file is missing, we cannot open it */
1171 : }
1172 1572892 : if (ret == -1) {
1173 : /* save the failed block for the check/fix */
1174 214224 : failed[failed_count].is_bad = 1;
1175 214224 : failed[failed_count].is_outofdate = 0;
1176 214224 : failed[failed_count].index = j;
1177 214224 : failed[failed_count].block = block;
1178 214224 : failed[failed_count].disk = disk;
1179 214224 : failed[failed_count].file = file;
1180 214224 : failed[failed_count].file_pos = file_pos;
1181 214224 : failed[failed_count].handle = &handle[j];
1182 214224 : ++failed_count;
1183 :
1184 214224 : log_tag("%s:%u:%s:%s: Open error at position %u. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, strerror(errno));
1185 :
1186 214224 : if (is_hw(errno)) {
1187 0 : ++io_error;
1188 : } else {
1189 214224 : ++soft_error;
1190 : }
1191 :
1192 : /*
1193 : * Mark the file as missing, to avoid to retry to open it again
1194 : * note that this can be done only if we are not fixing it
1195 : * otherwise, it could be recreated
1196 : */
1197 214224 : file_flag_set(file, FILE_IS_MISSING);
1198 214224 : continue;
1199 : }
1200 : }
1201 :
1202 : /* if it's the first open, and not excluded */
1203 2149259 : if (!file_flag_has(file, FILE_IS_OPENED)
1204 2132967 : && !file_flag_has(file, FILE_IS_EXCLUDED)) {
1205 :
1206 : /* check if the file is changed */
1207 2053770 : if (handle[j].st.st_size != file->size
1208 1850913 : || handle[j].st.st_mtime != file->mtime_sec
1209 1828045 : || STAT_NSEC(&handle[j].st) != file->mtime_nsec
1210 : /* don't check the inode to support file-system without persistent inodes */
1211 : ) {
1212 : /* report that the file is not synced */
1213 225725 : file_flag_set(file, FILE_IS_UNSYNCED);
1214 : }
1215 : }
1216 :
1217 : /* if it's the first open, and not excluded and larger */
1218 2149259 : if (!file_flag_has(file, FILE_IS_OPENED)
1219 2132967 : && !file_flag_has(file, FILE_IS_EXCLUDED)
1220 2053770 : && !(state->opt.syncedonly && file_flag_has(file, FILE_IS_UNSYNCED))
1221 2053770 : && handle[j].st.st_size > file->size
1222 : ) {
1223 8473 : log_error(ESOFT, "File '%s' is larger than expected.\n", handle[j].path);
1224 8473 : log_tag("error:%u:%s:%s: Size error\n", i, disk->name, esc_tag(file->sub, esc_buffer));
1225 8473 : ++soft_error;
1226 :
1227 8473 : if (fix) {
1228 3131 : ret = handle_truncate(&handle[j], file);
1229 3131 : if (ret == -1) {
1230 : /* LCOV_EXCL_START */
1231 : log_tag("%s:%u:%s:%s: Truncate error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
1232 : log_fatal_errno(errno, disk->name);
1233 : log_fatal(errno, "Stopping at block %u\n", i);
1234 :
1235 : ++unrecoverable_error;
1236 : goto bail;
1237 : /* LCOV_EXCL_STOP */
1238 : }
1239 :
1240 3131 : log_tag("fixed:%u:%s:%s: Fixed size\n", i, disk->name, esc_tag(file->sub, esc_buffer));
1241 3131 : ++recovered_error;
1242 : }
1243 : }
1244 :
1245 : /*
1246 : * Mark the file as opened at least one time
1247 : * this is used to avoid to check the unsynced and size
1248 : * more than one time, in case the file is reopened later
1249 : */
1250 2149259 : file_flag_set(file, FILE_IS_OPENED);
1251 : }
1252 :
1253 : /* read from the file */
1254 5220372 : if (file_flag_has(file, FILE_IS_MISSING)) {
1255 : /* if the file is reported missing, don't even try to read it */
1256 267179 : errno = ENOENT;
1257 267179 : read_size = -1;
1258 : } else {
1259 4953193 : read_size = handle_read(&handle[j], file_pos, buffer[j], state->block_size, log_error, state->opt.expected_missing ? log_expected : 0);
1260 : }
1261 5220372 : if (read_size == -1) {
1262 : /* save the failed block for the check/fix */
1263 463107 : failed[failed_count].is_bad = 1; /* it's bad because we cannot read it */
1264 463107 : failed[failed_count].is_outofdate = 0;
1265 463107 : failed[failed_count].index = j;
1266 463107 : failed[failed_count].block = block;
1267 463107 : failed[failed_count].disk = disk;
1268 463107 : failed[failed_count].file = file;
1269 463107 : failed[failed_count].file_pos = file_pos;
1270 463107 : failed[failed_count].handle = &handle[j];
1271 463107 : ++failed_count;
1272 :
1273 463107 : log_tag("%s:%u:%s:%s: Read error at position %u. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, strerror(errno));
1274 :
1275 463107 : if (is_hw(errno)) {
1276 0 : ++io_error;
1277 : } else {
1278 463107 : ++soft_error;
1279 : }
1280 :
1281 : /* if we are reading at the end, mark the file as missing to avoid to try to read it again at the next block */
1282 463107 : if (errno == ENOENT) {
1283 453263 : file_flag_set(file, FILE_IS_MISSING);
1284 : }
1285 463107 : continue;
1286 : }
1287 :
1288 4757265 : countsize += read_size;
1289 :
1290 : /*
1291 : * Always insert CHG blocks, the repair functions needs all of them
1292 : * because the parity may be still referring at the old state
1293 : * and the repair must be aware of it
1294 : */
1295 4757265 : if (block_state == BLOCK_STATE_CHG) {
1296 : /*
1297 : * We DO NOT mark them as bad to avoid to overwrite them with wrong data.
1298 : * if we don't have a hash, we always assume the first read of the block correct.
1299 : */
1300 16219 : failed[failed_count].is_bad = 0; /* we assume the CHG block correct */
1301 16219 : failed[failed_count].is_outofdate = 0;
1302 16219 : failed[failed_count].index = j;
1303 16219 : failed[failed_count].block = block;
1304 16219 : failed[failed_count].disk = disk;
1305 16219 : failed[failed_count].file = file;
1306 16219 : failed[failed_count].file_pos = file_pos;
1307 16219 : failed[failed_count].handle = &handle[j];
1308 16219 : ++failed_count;
1309 16219 : continue;
1310 : }
1311 :
1312 4741046 : assert(block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP);
1313 :
1314 : /* compute the hash of the block just read */
1315 4741046 : if (rehash) {
1316 27200 : memhash(state->prevhash, state->prevhashseed, hash, buffer[j], read_size);
1317 : } else {
1318 4713846 : memhash(state->hash, state->hashseed, hash, buffer[j], read_size);
1319 : }
1320 :
1321 : /* compare the hash */
1322 4741046 : if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
1323 13071 : unsigned diff = memdiff(hash, block->hash, BLOCK_HASH_SIZE);
1324 :
1325 : /* save the failed block for the check/fix */
1326 13071 : failed[failed_count].is_bad = 1; /* it's bad because the hash doesn't match */
1327 13071 : failed[failed_count].is_outofdate = 0;
1328 13071 : failed[failed_count].index = j;
1329 13071 : failed[failed_count].block = block;
1330 13071 : failed[failed_count].disk = disk;
1331 13071 : failed[failed_count].file = file;
1332 13071 : failed[failed_count].file_pos = file_pos;
1333 13071 : failed[failed_count].handle = &handle[j];
1334 13071 : ++failed_count;
1335 :
1336 13071 : log_tag("error:%u:%s:%s: Data error at position %u, diff hash bits %u/%u\n", i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, diff, BLOCK_HASH_SIZE * 8);
1337 13071 : ++silent_error;
1338 13071 : continue;
1339 : }
1340 :
1341 : /*
1342 : * Always insert REP blocks, the repair functions needs all of them
1343 : * because the parity may be still referring at the old state
1344 : * and the repair must be aware of it
1345 : */
1346 4727975 : if (block_state == BLOCK_STATE_REP) {
1347 52148 : failed[failed_count].is_bad = 0; /* it's not bad */
1348 52148 : failed[failed_count].is_outofdate = 0;
1349 52148 : failed[failed_count].index = j;
1350 52148 : failed[failed_count].block = block;
1351 52148 : failed[failed_count].disk = disk;
1352 52148 : failed[failed_count].file = file;
1353 52148 : failed[failed_count].file_pos = file_pos;
1354 52148 : failed[failed_count].handle = &handle[j];
1355 52148 : ++failed_count;
1356 52148 : continue;
1357 : }
1358 : }
1359 :
1360 : /* now read and check the parity if requested */
1361 953182 : if (!state->opt.auditonly) {
1362 : void* buffer_recov[LEV_MAX];
1363 : void* buffer_zero;
1364 :
1365 : /* buffers for parity read and not computed */
1366 4353661 : for (l = 0; l < state->level; ++l)
1367 3439994 : buffer_recov[l] = buffer[diskmax + state->level + l];
1368 2955675 : for (; l < LEV_MAX; ++l)
1369 2042008 : buffer_recov[l] = 0;
1370 :
1371 : /* the zero buffer is the last one */
1372 913667 : buffer_zero = buffer[buffermax - 1];
1373 :
1374 : /* read the parity */
1375 4353661 : for (l = 0; l < state->level; ++l) {
1376 3439994 : if (parity[l]) {
1377 3426032 : ret = parity_read(parity[l], i, buffer_recov[l], state->block_size, log_error);
1378 3426032 : if (ret == -1) {
1379 96822 : log_tag("parity_%s:%u:%s: Read error. %s.\n", es(errno), i, lev_config_name(l), strerror(errno));
1380 :
1381 96822 : buffer_recov[l] = 0; /* no parity to use */
1382 :
1383 96822 : if (is_hw(errno)) {
1384 0 : ++io_error;
1385 : } else {
1386 96822 : ++soft_error;
1387 : }
1388 : }
1389 : } else {
1390 13962 : buffer_recov[l] = 0;
1391 : }
1392 : }
1393 :
1394 : /* try all the recovering strategies */
1395 913667 : ret = repair(state, rehash, i, diskmax, failed, failed_map, failed_count, buffer, buffer_recov, buffer_zero);
1396 913667 : if (ret != 0) {
1397 : /* increment the number of errors */
1398 47843 : if (ret > 0)
1399 4936 : silent_error += ret;
1400 47843 : ++unrecoverable_error;
1401 :
1402 : /* print a list of all the errors in files */
1403 218382 : for (j = 0; j < failed_count; ++j) {
1404 170539 : if (failed[j].is_bad)
1405 165976 : log_tag("unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].disk->name, esc_tag(failed[j].file->sub, esc_buffer), failed[j].file_pos);
1406 : }
1407 :
1408 : /* keep track of damaged files */
1409 218382 : for (j = 0; j < failed_count; ++j) {
1410 170539 : if (failed[j].is_bad)
1411 165976 : file_flag_set(failed[j].file, FILE_IS_DAMAGED);
1412 : }
1413 : } else {
1414 : /*
1415 : * Now counts partial recovers
1416 : * note that this could happen only when we have an incomplete 'sync'
1417 : * and that we have recovered is the state before the 'sync'
1418 : */
1419 865824 : int partial_recover_error = 0;
1420 :
1421 : /* print a list of all the errors in files */
1422 1467435 : for (j = 0; j < failed_count; ++j) {
1423 601611 : if (failed[j].is_bad && failed[j].is_outofdate) {
1424 8 : ++partial_recover_error;
1425 8 : log_tag("unrecoverable:%u:%s:%s: Unrecoverable unsynced error at position %u\n", i, failed[j].disk->name, esc_tag(failed[j].file->sub, esc_buffer), failed[j].file_pos);
1426 : }
1427 : }
1428 865824 : if (partial_recover_error != 0) {
1429 8 : silent_error += partial_recover_error;
1430 8 : ++unrecoverable_error;
1431 : }
1432 :
1433 : /*
1434 : * Check parities, but only if all the blocks have it computed and it's used.
1435 : *
1436 : * If you check/fix after a partial sync, it's OK to have parity errors
1437 : * on the blocks with invalid parity and doesn't make sense to try to fix it.
1438 : *
1439 : * It's also OK to have data errors on unused parity, because sync doesn't
1440 : * update it.
1441 : */
1442 865824 : if (used_parity && valid_parity) {
1443 : /* check the parity */
1444 3906083 : for (l = 0; l < state->level; ++l) {
1445 3104571 : if (buffer_recov[l] != 0 && memcmp(buffer_recov[l], buffer[diskmax + l], state->block_size) != 0) {
1446 21631 : unsigned diff = memdiff(buffer_recov[l], buffer[diskmax + l], state->block_size);
1447 :
1448 : /* mark that the read parity is wrong, setting ptr to 0 */
1449 21631 : buffer_recov[l] = 0;
1450 :
1451 21631 : log_tag("parity_error:%u:%s: Data error, diff parity bits %u/%u\n", i, lev_config_name(l), diff, state->block_size * 8);
1452 21631 : ++silent_error;
1453 : }
1454 : }
1455 : }
1456 :
1457 : /* now write recovered files */
1458 865824 : if (fix) {
1459 : /* update the fixed files */
1460 696842 : for (j = 0; j < failed_count; ++j) {
1461 : /* nothing to do if it doesn't need recovering */
1462 361058 : if (!failed[j].is_bad)
1463 41411 : continue;
1464 :
1465 : /* do not fix if the file is excluded */
1466 319647 : if (file_flag_has(failed[j].file, FILE_IS_EXCLUDED)
1467 310483 : || (state->opt.syncedonly && file_flag_has(failed[j].file, FILE_IS_UNSYNCED)))
1468 9164 : continue;
1469 :
1470 310483 : ret = handle_write(failed[j].handle, failed[j].file_pos, buffer[failed[j].index], state->block_size);
1471 310483 : if (ret == -1) {
1472 : /* LCOV_EXCL_START */
1473 : log_tag("%s:%u:%s:%s: Write error. %s.\n", es(errno), i, failed[j].disk->name, esc_tag(failed[j].file->sub, esc_buffer), strerror(errno));
1474 : log_fatal_errno(errno, failed[j].disk->name);
1475 : log_fatal(errno, "Stopping at block %u\n", i);
1476 :
1477 : /* mark the file as damaged */
1478 : file_flag_set(failed[j].file, FILE_IS_DAMAGED);
1479 :
1480 : ++unrecoverable_error;
1481 : goto bail;
1482 : /* LCOV_EXCL_STOP */
1483 : }
1484 :
1485 : /* if we are not sure that the recovered content is uptodate */
1486 310483 : if (failed[j].is_outofdate) {
1487 : /* mark the file as damaged */
1488 8 : file_flag_set(failed[j].file, FILE_IS_DAMAGED);
1489 8 : continue;
1490 : }
1491 :
1492 : /*
1493 : * Mark the file as containing some fixes
1494 : * note that it could be also marked as damaged in other iterations
1495 : */
1496 310475 : file_flag_set(failed[j].file, FILE_IS_FIXED);
1497 :
1498 310475 : log_tag("fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].disk->name, esc_tag(failed[j].file->sub, esc_buffer), failed[j].file_pos);
1499 310475 : ++recovered_error;
1500 : }
1501 :
1502 : /*
1503 : * Update parity only if all the blocks have it computed and it's used.
1504 : *
1505 : * If you check/fix after a partial sync, you do not want to fix parity
1506 : * for blocks that are going to have it computed in the sync completion.
1507 : *
1508 : * For unused parity there is no need to write it, because when fixing
1509 : * we already have allocated space for it on parity file creation,
1510 : * and its content doesn't matter.
1511 : */
1512 335784 : if (used_parity && valid_parity) {
1513 : /* update the parity */
1514 1048077 : for (l = 0; l < state->level; ++l) {
1515 : /* if the parity on disk is wrong */
1516 762196 : if (buffer_recov[l] == 0
1517 : /* and we have access at the parity */
1518 79589 : && parity[l] != 0
1519 : /* and the parity is not excluded */
1520 74902 : && !state->parity[l].is_excluded_by_filter
1521 : ) {
1522 74902 : ret = parity_write(parity[l], i, buffer[diskmax + l], state->block_size);
1523 74902 : if (ret == -1) {
1524 : /* LCOV_EXCL_START */
1525 : log_tag("%s:%u:%s: Write error. %s.\n", es(errno), i, lev_config_name(l), strerror(errno));
1526 : log_fatal_errno(errno, lev_config_name(l));
1527 : log_fatal(errno, "Stopping at block %u\n", i);
1528 :
1529 : ++unrecoverable_error;
1530 : goto bail;
1531 : /* LCOV_EXCL_STOP */
1532 : }
1533 :
1534 74902 : log_tag("parity_fixed:%u:%s: Fixed data error\n", i, lev_config_name(l));
1535 74902 : ++recovered_error;
1536 : }
1537 : }
1538 : }
1539 : } else {
1540 : /*
1541 : * If we are not fixing, we just set the FIXED flag
1542 : * meaning that we could fix this file if we try
1543 : */
1544 770593 : for (j = 0; j < failed_count; ++j) {
1545 240553 : if (failed[j].is_bad) {
1546 198726 : file_flag_set(failed[j].file, FILE_IS_FIXED);
1547 : }
1548 : }
1549 : }
1550 : }
1551 : } else {
1552 : /*
1553 : * If we are not checking, we just set the DAMAGED flag
1554 : * to report that the file is damaged, and we don't know if we can fix it
1555 : */
1556 45568 : for (j = 0; j < failed_count; ++j) {
1557 6053 : if (failed[j].is_bad) {
1558 6053 : file_flag_set(failed[j].file, FILE_IS_DAMAGED);
1559 : }
1560 : }
1561 : }
1562 :
1563 : /* post process the files */
1564 953182 : ret = file_post(state, fix, i, handle, diskmax);
1565 953182 : if (ret == -1) {
1566 : /* LCOV_EXCL_START */
1567 : log_fatal(errno, "Stopping at block %u\n", i);
1568 :
1569 : ++unrecoverable_error;
1570 : goto bail;
1571 : /* LCOV_EXCL_STOP */
1572 : }
1573 :
1574 : /* count the number of processed block */
1575 953182 : ++countpos;
1576 :
1577 : /* progress */
1578 953182 : if (state_progress(state, 0, i, countpos, countmax, countsize)) {
1579 : /* LCOV_EXCL_START */
1580 : break;
1581 : /* LCOV_EXCL_STOP */
1582 : }
1583 :
1584 : /* thermal control */
1585 953182 : if (state_thermal_alarm(state)) {
1586 : /* until now is misc */
1587 0 : state_usage_misc(state);
1588 :
1589 0 : state_progress_stop(state);
1590 :
1591 0 : state_thermal_cooldown(state);
1592 :
1593 0 : state_progress_restart(state);
1594 :
1595 : /* drop until now */
1596 0 : state_usage_waste(state);
1597 : }
1598 : }
1599 :
1600 : /* for each disk, recover empty files, symlinks and empty dirs */
1601 894 : for (i = 0; i < diskmax; ++i) {
1602 : tommy_node* node;
1603 : struct snapraid_disk* disk;
1604 :
1605 762 : if (!handle[i].disk)
1606 3 : continue;
1607 :
1608 : /* for each empty file in the disk */
1609 759 : disk = handle[i].disk;
1610 759 : node = disk->filelist;
1611 2276845 : while (node) {
1612 : char path[PATH_MAX];
1613 : struct stat st;
1614 : struct snapraid_file* file;
1615 2276086 : int unsuccessful = 0;
1616 :
1617 2276086 : file = node->data;
1618 2276086 : node = node->next; /* next node */
1619 :
1620 : /* if not empty, it's already checked and continue to the next one */
1621 2276086 : if (file->size != 0) {
1622 2273217 : continue;
1623 : }
1624 :
1625 : /* if excluded continue to the next one */
1626 3071 : if (file_flag_has(file, FILE_IS_EXCLUDED)) {
1627 202 : continue;
1628 : }
1629 :
1630 : /* stat the file */
1631 2869 : pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
1632 2869 : ret = stat(path, &st);
1633 2869 : if (ret == -1) {
1634 197 : unsuccessful = 1;
1635 :
1636 197 : log_error(errno, "Error stating empty file '%s'. %s.\n", path, strerror(errno));
1637 197 : log_tag("empty_%s:%s:%s: Empty file stat error\n", es(errno), disk->name, esc_tag(file->sub, esc_buffer));
1638 :
1639 197 : if (is_hw(errno)) {
1640 0 : ++io_error;
1641 : } else {
1642 197 : ++soft_error;
1643 : }
1644 2672 : } else if (!S_ISREG(st.st_mode)) {
1645 0 : unsuccessful = 1;
1646 :
1647 0 : log_error(ESOFT, "Error stating empty file '%s' for not regular file.\n", path);
1648 0 : log_tag("empty_error:%s:%s: Empty file error for not regular file\n", disk->name, esc_tag(file->sub, esc_buffer));
1649 0 : ++soft_error;
1650 2672 : } else if (st.st_size != 0) {
1651 20 : unsuccessful = 1;
1652 :
1653 20 : log_error(ESOFT, "Error stating empty file '%s' for not empty file.\n", path);
1654 20 : log_tag("empty_error:%s:%s: Empty file error for size '%" PRIu64 "'\n", disk->name, esc_tag(file->sub, esc_buffer), (uint64_t)st.st_size);
1655 20 : ++soft_error;
1656 : }
1657 :
1658 2869 : if (fix && unsuccessful) {
1659 : int f;
1660 :
1661 : /* create the ancestor directories */
1662 174 : ret = mkancestor(path);
1663 174 : if (ret != 0) {
1664 : /* LCOV_EXCL_START */
1665 : log_fatal(errno, "Error creating ancestor '%s%s'. %s.\n", disk->dir, file->sub, strerror(errno));
1666 : log_tag("empty_%s:%u:%s:%s: Create ancestor error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
1667 : log_fatal_errno(errno, disk->name);
1668 : log_fatal(errno, "Stopping\n");
1669 :
1670 : ++unrecoverable_error;
1671 : goto bail;
1672 : /* LCOV_EXCL_STOP */
1673 : }
1674 :
1675 : /*
1676 : * Create it
1677 : * O_NOFOLLOW: do not follow links to ensure to open the real file
1678 : */
1679 174 : f = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_NOFOLLOW, 0600);
1680 174 : if (f == -1) {
1681 : /* LCOV_EXCL_START */
1682 : log_fatal(errno, "Error creating '%s%s'. %s.\n", disk->dir, file->sub, strerror(errno));
1683 : log_tag("empty_%s:%u:%s:%s: Create error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
1684 : log_fatal_errno(errno, disk->name);
1685 : log_fatal(errno, "Stopping\n");
1686 :
1687 : ++unrecoverable_error;
1688 : goto bail;
1689 : /* LCOV_EXCL_STOP */
1690 : }
1691 :
1692 : /* set the original modification time */
1693 174 : ret = fmtime(f, file->mtime_sec, file->mtime_nsec);
1694 174 : if (ret != 0) {
1695 : /* LCOV_EXCL_START */
1696 : log_fatal(errno, "Error timing '%s%s'. %s.\n", disk->dir, file->sub, strerror(errno));
1697 : log_tag("empty_%s:%u:%s:%s: Time error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
1698 : log_fatal_errno(errno, disk->name);
1699 : log_fatal(errno, "Stopping\n");
1700 :
1701 : close(f);
1702 :
1703 : ++unrecoverable_error;
1704 : goto bail;
1705 : /* LCOV_EXCL_STOP */
1706 : }
1707 :
1708 : /* close it */
1709 174 : ret = close(f);
1710 174 : if (ret != 0) {
1711 : /* LCOV_EXCL_START */
1712 : log_fatal(errno, "Error closing '%s%s'. %s.\n", disk->dir, file->sub, strerror(errno));
1713 : log_tag("empty_%s:%u:%s:%s: Close error. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
1714 : log_fatal_errno(errno, disk->name);
1715 : log_fatal(errno, "Stopping\n");
1716 :
1717 : ++unrecoverable_error;
1718 : goto bail;
1719 : /* LCOV_EXCL_STOP */
1720 : }
1721 :
1722 174 : log_tag("empty_fixed:%s:%s: Fixed empty file\n", disk->name, esc_tag(file->sub, esc_buffer));
1723 174 : ++recovered_error;
1724 :
1725 174 : log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
1726 174 : msg_info("recovered %s\n", fmt_term(disk, file->sub, esc_buffer));
1727 : }
1728 : }
1729 :
1730 : /* for each link in the disk */
1731 759 : disk = handle[i].disk;
1732 759 : node = disk->linklist;
1733 73578 : while (node) {
1734 : char path[PATH_MAX];
1735 : char pathto[PATH_MAX];
1736 : char linkto[PATH_MAX];
1737 : struct stat st;
1738 : struct stat stto;
1739 : struct snapraid_link* slink;
1740 72819 : int unsuccessful = 0;
1741 72819 : int unrecoverable = 0;
1742 :
1743 72819 : slink = node->data;
1744 72819 : node = node->next; /* next node */
1745 :
1746 : /* if excluded continue to the next one */
1747 72819 : if (link_flag_has(slink, FILE_IS_EXCLUDED)) {
1748 4169 : continue;
1749 : }
1750 :
1751 68650 : if (link_flag_has(slink, FILE_IS_HARDLINK)) {
1752 : /* stat the link */
1753 327 : pathprint(path, sizeof(path), "%s%s", disk->dir, slink->sub);
1754 327 : ret = stat(path, &st);
1755 327 : if (ret == -1) {
1756 36 : unsuccessful = 1;
1757 :
1758 36 : log_error(errno, "Error stating hardlink '%s'. %s.\n", path, strerror(errno));
1759 36 : log_tag("hardlink_%s:%s:%s:%s: Hardlink stat error. %s.\n", es(errno), disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt), strerror(errno));
1760 :
1761 36 : if (is_hw(errno)) {
1762 0 : ++io_error;
1763 : } else {
1764 36 : ++soft_error;
1765 : }
1766 291 : } else if (!S_ISREG(st.st_mode)) {
1767 0 : unsuccessful = 1;
1768 :
1769 0 : log_error(ESOFT, "Error stating hardlink '%s' for not regular file.\n", path);
1770 0 : log_tag("hardlink_error:%s:%s:%s: Hardlink error for not regular file\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
1771 0 : ++soft_error;
1772 : }
1773 :
1774 : /* stat the "to" file */
1775 327 : pathprint(pathto, sizeof(pathto), "%s%s", disk->dir, slink->linkto);
1776 327 : ret = stat(pathto, &stto);
1777 327 : if (ret == -1) {
1778 24 : unsuccessful = 1;
1779 :
1780 24 : if (errno == ENOENT) {
1781 24 : unrecoverable = 1;
1782 24 : if (fix) {
1783 : /*
1784 : * If the target doesn't exist, it's unrecoverable
1785 : * because we cannot create an hardlink of a file that
1786 : * doesn't exists
1787 : * but in check, we can assume that fixing will recover
1788 : * such missing file, so we assume a less drastic error
1789 : */
1790 12 : ++unrecoverable_error;
1791 : }
1792 : }
1793 :
1794 24 : log_error(errno, "Error stating hardlink-to '%s'. %s.\n", pathto, strerror(errno));
1795 24 : log_tag("hardlink_%s:%s:%s:%s: Hardlink to stat error. %s.\n", es(errno), disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt), strerror(errno));
1796 :
1797 24 : if (is_hw(errno)) {
1798 0 : ++io_error;
1799 : } else {
1800 24 : ++soft_error;
1801 : }
1802 303 : } else if (!S_ISREG(stto.st_mode)) {
1803 0 : unsuccessful = 1;
1804 :
1805 0 : log_error(ESOFT, "Error stating hardlink-to '%s' for not regular file.\n", path);
1806 0 : log_tag("hardlink_error:%s:%s:%s: Hardlink-to error for not regular file\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
1807 0 : ++soft_error;
1808 303 : } else if (!unsuccessful && st.st_ino != stto.st_ino) {
1809 0 : unsuccessful = 1;
1810 :
1811 0 : log_error(ESOFT, "Mismatch hardlink '%s' and '%s'. Different inode.\n", path, pathto);
1812 0 : log_tag("hardlink_error:%s:%s:%s: Hardlink mismatch for different inode\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
1813 0 : ++soft_error;
1814 : }
1815 : } else {
1816 : /* read the symlink */
1817 68323 : pathprint(path, sizeof(path), "%s%s", disk->dir, slink->sub);
1818 68323 : ret = readlink(path, linkto, sizeof(linkto));
1819 68323 : if (ret < 0) {
1820 5020 : unsuccessful = 1;
1821 :
1822 5020 : log_error(errno, "Error reading symlink '%s'. %s.\n", path, strerror(errno));
1823 5020 : log_tag("symlink_%s:%s:%s: Symlink read error. %s.\n", es(errno), disk->name, esc_tag(slink->sub, esc_buffer), strerror(errno));
1824 :
1825 5020 : if (is_hw(errno)) {
1826 0 : ++io_error;
1827 : } else {
1828 5020 : ++soft_error;
1829 : }
1830 63303 : } else if (ret >= PATH_MAX) {
1831 0 : unsuccessful = 1;
1832 :
1833 0 : log_error(ESOFT, "Error reading symlink '%s'. Symlink too long.\n", path);
1834 0 : log_tag("symlink_error:%s:%s: Symlink too long\n", disk->name, esc_tag(slink->sub, esc_buffer));
1835 0 : ++soft_error;
1836 : } else {
1837 63303 : linkto[ret] = 0;
1838 :
1839 63303 : if (strcmp(linkto, slink->linkto) != 0) {
1840 499 : unsuccessful = 1;
1841 :
1842 499 : log_tag("symlink_error:%s:%s: Symlink data error '%s' instead of '%s'\n", disk->name, esc_tag(slink->sub, esc_buffer), linkto, slink->linkto);
1843 499 : ++soft_error;
1844 : }
1845 : }
1846 : }
1847 :
1848 68650 : if (fix && unsuccessful && !unrecoverable) {
1849 4340 : const char* link = link_flag_has(slink, FILE_IS_HARDLINK) ? "hard" : "sym";
1850 :
1851 : /* create the ancestor directories */
1852 4340 : ret = mkancestor(path);
1853 4340 : if (ret != 0) {
1854 : /* LCOV_EXCL_START */
1855 : log_fatal(errno, "Error creating ancestor '%s%s'. %s.\n", disk->dir, slink->sub, strerror(errno));
1856 : log_tag("%slink_%s:%u:%s:%s: Create ancestor error. %s.\n", link, es(errno), i, disk->name, esc_tag(slink->sub, esc_buffer), strerror(errno));
1857 : log_fatal_errno(errno, disk->name);
1858 : log_fatal(errno, "Stopping\n");
1859 :
1860 : ++unrecoverable_error;
1861 : goto bail;
1862 : /* LCOV_EXCL_STOP */
1863 : }
1864 :
1865 : /* if it exists, it must be deleted before recreating */
1866 4340 : ret = remove(path);
1867 4340 : if (ret != 0 && errno != ENOENT) {
1868 : /* LCOV_EXCL_START */
1869 : log_fatal(errno, "Error removing '%s%s'. %s.\n", disk->dir, slink->sub, strerror(errno));
1870 : log_tag("%slink_%s:%u:%s:%s: Remove error. %s.\n", link, es(errno), i, disk->name, esc_tag(slink->sub, esc_buffer), strerror(errno));
1871 : log_fatal_errno(errno, disk->name);
1872 : log_fatal(errno, "Stopping\n");
1873 :
1874 : ++unrecoverable_error;
1875 : goto bail;
1876 : /* LCOV_EXCL_STOP */
1877 : }
1878 :
1879 : /* create it */
1880 4340 : if (link_flag_has(slink, FILE_IS_HARDLINK)) {
1881 12 : ret = hardlink(pathto, path);
1882 12 : if (ret != 0) {
1883 : /* LCOV_EXCL_START */
1884 : log_fatal(errno, "Error writing hardlink '%s' to '%s'. %s.\n", path, pathto, strerror(errno));
1885 : log_tag("hardlink_%s:%u:%s:%s: Hardlink error. %s.\n", es(errno), i, disk->name, esc_tag(slink->sub, esc_buffer), strerror(errno));
1886 : log_fatal_errno(errno, disk->name);
1887 : log_fatal(errno, "Stopping\n");
1888 :
1889 : ++unrecoverable_error;
1890 : goto bail;
1891 : /* LCOV_EXCL_STOP */
1892 : }
1893 :
1894 12 : log_tag("hardlink_fixed:%s:%s: Fixed hardlink error\n", disk->name, esc_tag(slink->sub, esc_buffer));
1895 12 : ++recovered_error;
1896 : } else {
1897 4328 : ret = symlink(slink->linkto, path);
1898 4328 : if (ret != 0) {
1899 : /* LCOV_EXCL_START */
1900 : log_fatal(errno, "Error writing symlink '%s' to '%s'. %s.\n", path, slink->linkto, strerror(errno));
1901 : log_tag("symlink_%s:%u:%s:%s: Hardlink error. %s.\n", es(errno), i, disk->name, esc_tag(slink->sub, esc_buffer), strerror(errno));
1902 : log_fatal_errno(errno, disk->name);
1903 : log_fatal(errno, "Stopping\n");
1904 :
1905 : ++unrecoverable_error;
1906 : goto bail;
1907 : /* LCOV_EXCL_STOP */
1908 : }
1909 :
1910 4328 : log_tag("symlink_fixed:%s:%s: Fixed symlink error\n", disk->name, esc_tag(slink->sub, esc_buffer));
1911 4328 : ++recovered_error;
1912 : }
1913 :
1914 4340 : log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(slink->sub, esc_buffer));
1915 4340 : msg_info("recovered %s\n", fmt_term(disk, slink->sub, esc_buffer));
1916 : }
1917 : }
1918 :
1919 : /* for each dir in the disk */
1920 759 : disk = handle[i].disk;
1921 759 : node = disk->dirlist;
1922 1215 : while (node) {
1923 : char path[PATH_MAX];
1924 : struct stat st;
1925 : struct snapraid_dir* dir;
1926 456 : int unsuccessful = 0;
1927 :
1928 456 : dir = node->data;
1929 456 : node = node->next; /* next node */
1930 :
1931 : /* if excluded continue to the next one */
1932 456 : if (dir_flag_has(dir, FILE_IS_EXCLUDED)) {
1933 23 : continue;
1934 : }
1935 :
1936 : /* stat the dir */
1937 433 : pathprint(path, sizeof(path), "%s%s", disk->dir, dir->sub);
1938 433 : ret = stat(path, &st);
1939 433 : if (ret == -1) {
1940 27 : unsuccessful = 1;
1941 :
1942 27 : log_error(errno, "Error stating dir '%s'. %s.\n", path, strerror(errno));
1943 27 : log_tag("dir_%s:%s:%s: Dir stat error. %s.\n", es(errno), disk->name, esc_tag(dir->sub, esc_buffer), strerror(errno));
1944 :
1945 27 : if (is_hw(errno)) {
1946 0 : ++io_error;
1947 : } else {
1948 27 : ++soft_error;
1949 : }
1950 406 : } else if (!S_ISDIR(st.st_mode)) {
1951 0 : unsuccessful = 1;
1952 :
1953 0 : log_tag("dir_error:%s:%s: Dir error for not directory\n", disk->name, esc_tag(dir->sub, esc_buffer));
1954 0 : ++soft_error;
1955 : }
1956 :
1957 433 : if (fix && unsuccessful) {
1958 : /* create the ancestor directories */
1959 23 : ret = mkancestor(path);
1960 23 : if (ret != 0) {
1961 : /* LCOV_EXCL_START */
1962 : log_fatal(errno, "Error creating ancestor '%s%s'. %s.\n", disk->dir, dir->sub, strerror(errno));
1963 : log_tag("dir_%s:%u:%s:%s: Create ancestor error. %s.\n", es(errno), i, disk->name, esc_tag(dir->sub, esc_buffer), strerror(errno));
1964 : log_fatal_errno(errno, disk->name);
1965 : log_fatal(errno, "Stopping\n");
1966 :
1967 : ++unrecoverable_error;
1968 : goto bail;
1969 : /* LCOV_EXCL_STOP */
1970 : }
1971 :
1972 : /* create it */
1973 23 : ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
1974 23 : if (ret != 0) {
1975 : /* LCOV_EXCL_START */
1976 : log_fatal(errno, "Error creating directory '%s%s'. %s.\n", disk->dir, dir->sub, strerror(errno));
1977 : log_tag("dir_%s:%u:%s:%s: Create directory error. %s.\n", es(errno), i, disk->name, esc_tag(dir->sub, esc_buffer), strerror(errno));
1978 : log_fatal_errno(errno, disk->name);
1979 : log_fatal(errno, "Stopping\n");
1980 :
1981 : ++unrecoverable_error;
1982 : goto bail;
1983 : /* LCOV_EXCL_STOP */
1984 : }
1985 :
1986 23 : log_tag("dir_fixed:%s:%s: Fixed dir error\n", disk->name, esc_tag(dir->sub, esc_buffer));
1987 23 : ++recovered_error;
1988 :
1989 23 : log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(dir->sub, esc_buffer));
1990 23 : msg_info("recovered %s\n", fmt_term(disk, dir->sub, esc_buffer));
1991 : }
1992 : }
1993 : }
1994 :
1995 132 : end:
1996 132 : state_progress_end(state, countpos, countmax, countsize, "Nothing to check.\n");
1997 :
1998 132 : bail:
1999 : /* close all the files left open */
2000 894 : for (j = 0; j < diskmax; ++j) {
2001 762 : struct snapraid_file* file = handle[j].file;
2002 762 : struct snapraid_disk* disk = handle[j].disk;
2003 762 : ret = handle_close(&handle[j]);
2004 762 : if (ret == -1) {
2005 : /* LCOV_EXCL_START */
2006 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockmax, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
2007 : log_fatal_errno(errno, disk->name);
2008 :
2009 : ++unrecoverable_error;
2010 : /* continue, as we are already exiting */
2011 : /* LCOV_EXCL_STOP */
2012 : }
2013 : }
2014 :
2015 : /*
2016 : * Remove all the files created from scratch that have not finished the processing
2017 : * it happens only when aborting pressing Ctrl+C or other reason.
2018 : */
2019 132 : if (fix) {
2020 : /* for each disk */
2021 345 : for (i = 0; i < diskmax; ++i) {
2022 : tommy_node* node;
2023 : struct snapraid_disk* disk;
2024 :
2025 294 : if (!handle[i].disk)
2026 1 : continue;
2027 :
2028 : /* for each file in the disk */
2029 293 : disk = handle[i].disk;
2030 293 : node = disk->filelist;
2031 912853 : while (node) {
2032 : char path[PATH_MAX];
2033 : struct snapraid_file* file;
2034 :
2035 912560 : file = node->data;
2036 912560 : node = node->next; /* next node */
2037 :
2038 : /* if the file was not created, meaning that it was already existing */
2039 912560 : if (!file_flag_has(file, FILE_IS_CREATED)) {
2040 : /* nothing to do */
2041 912560 : continue;
2042 : }
2043 :
2044 : /* if processing was finished */
2045 123783 : if (file_flag_has(file, FILE_IS_FINISHED)) {
2046 : /* nothing to do */
2047 123783 : continue;
2048 : }
2049 :
2050 : /*
2051 : * If the file was originally missing, and processing not yet finished
2052 : * we have to throw it away to ensure that at the next run we will retry
2053 : * to fix it, in case we select to undelete missing files
2054 : */
2055 0 : pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
2056 :
2057 0 : ret = remove(path);
2058 0 : if (ret != 0) {
2059 : /* LCOV_EXCL_START */
2060 : log_fatal(errno, "Error removing '%s'. %s.\n", path, strerror(errno));
2061 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockmax, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
2062 : log_fatal_errno(errno, disk->name);
2063 :
2064 : ++unrecoverable_error;
2065 : /* continue, as we are already exiting */
2066 : /* LCOV_EXCL_STOP */
2067 : }
2068 : }
2069 : }
2070 : }
2071 :
2072 132 : if (soft_error || io_error || silent_error || recovered_error || unrecoverable_error) {
2073 75 : msg_status("\n");
2074 75 : msg_status("%8u soft errors\n", soft_error);
2075 75 : msg_status("%8u io errors\n", io_error);
2076 75 : msg_status("%8u data errors\n", silent_error);
2077 75 : if (fix) {
2078 50 : msg_status("%8u recovered errors\n", recovered_error);
2079 : }
2080 75 : if (unrecoverable_error) {
2081 13 : msg_status("%8u UNRECOVERABLE errors\n", unrecoverable_error);
2082 : } else {
2083 : /* without checking, we don't know if they are really recoverable or not */
2084 62 : if (!state->opt.auditonly)
2085 60 : msg_status("%8u unrecoverable errors\n", unrecoverable_error);
2086 62 : if (fix)
2087 41 : msg_status("Everything OK\n");
2088 : }
2089 : } else {
2090 57 : msg_status("Everything OK\n");
2091 : }
2092 :
2093 132 : if ((soft_error || io_error || silent_error) && !fix) {
2094 25 : if (soft_error)
2095 22 : log_fatal(ESOFT, "WARNING! There are soft errors!\n");
2096 25 : if (io_error)
2097 0 : log_fatal(EIO, "DANGER! Unexpected input/output errors!\n");
2098 25 : if (silent_error)
2099 13 : log_fatal(EDATA, "DANGER! Unexpected silent data errors!\n");
2100 : }
2101 132 : if (unrecoverable_error)
2102 13 : log_fatal(ESOFT, "DANGER! Unrecoverable errors detected!\n");
2103 :
2104 132 : log_tag("summary:error_soft:%u\n", soft_error);
2105 132 : log_tag("summary:error_io:%u\n", io_error);
2106 132 : log_tag("summary:error_data:%u\n", silent_error);
2107 132 : if (fix)
2108 51 : log_tag("summary:error_recovered:%u\n", recovered_error);
2109 132 : if (!state->opt.auditonly)
2110 127 : log_tag("summary:error_unrecoverable:%u\n", unrecoverable_error);
2111 132 : if (fix) {
2112 51 : if (soft_error + io_error + silent_error + recovered_error + unrecoverable_error == 0)
2113 1 : log_tag("summary:exit:ok\n");
2114 50 : else if (unrecoverable_error == 0)
2115 41 : log_tag("summary:exit:recovered\n");
2116 : else
2117 9 : log_tag("summary:exit:unrecoverable\n");
2118 81 : } else if (!state->opt.auditonly) {
2119 76 : if (soft_error + io_error + silent_error + unrecoverable_error == 0)
2120 53 : log_tag("summary:exit:ok\n");
2121 23 : else if (unrecoverable_error == 0)
2122 19 : log_tag("summary:exit:recoverable\n");
2123 : else
2124 4 : log_tag("summary:exit:unrecoverable\n");
2125 : } else { /* audit only */
2126 5 : if (soft_error + silent_error + io_error == 0)
2127 3 : log_tag("summary:exit:ok\n");
2128 2 : else if (silent_error + io_error == 0)
2129 1 : log_tag("summary:exit:warning\n");
2130 : else
2131 1 : log_tag("summary:exit:error\n");
2132 : }
2133 132 : log_flush();
2134 :
2135 132 : free(failed);
2136 132 : free(failed_map);
2137 132 : free(block_enabled);
2138 132 : free(handle);
2139 132 : free(buffer_alloc);
2140 132 : free(buffer);
2141 :
2142 : /* fail if some error are present after the run */
2143 132 : if (fix) {
2144 51 : if (state->opt.expect_unrecoverable) {
2145 9 : if (unrecoverable_error == 0)
2146 0 : return -1;
2147 : } else {
2148 42 : if (unrecoverable_error != 0)
2149 0 : return -1;
2150 : }
2151 : } else {
2152 81 : if (state->opt.expect_unrecoverable) {
2153 4 : if (unrecoverable_error == 0)
2154 0 : return -1;
2155 77 : } else if (state->opt.expect_recoverable) {
2156 21 : if (unrecoverable_error != 0)
2157 0 : return -1;
2158 21 : if (soft_error + silent_error + io_error == 0)
2159 0 : return -1;
2160 : } else {
2161 56 : if (unrecoverable_error != 0)
2162 0 : return -1;
2163 56 : if (soft_error + silent_error + io_error != 0)
2164 0 : return -1;
2165 : }
2166 : }
2167 :
2168 132 : if (alert < 0)
2169 0 : return -1;
2170 :
2171 132 : return 0;
2172 : }
2173 :
2174 134 : int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, block_off_t blockcount)
2175 : {
2176 : block_off_t blockmax;
2177 : data_off_t size;
2178 : int ret;
2179 : struct snapraid_parity_handle parity[LEV_MAX];
2180 : struct snapraid_parity_handle* parity_ptr[LEV_MAX];
2181 : unsigned process_error;
2182 : unsigned l;
2183 :
2184 134 : msg_progress("Initializing...\n");
2185 :
2186 134 : blockmax = parity_allocated_size(state);
2187 134 : size = blockmax * (data_off_t)state->block_size;
2188 :
2189 134 : if (blockstart > blockmax) {
2190 : /* LCOV_EXCL_START */
2191 : log_fatal(EUSER, "Error in the specified starting block %u. It's larger than the parity size %u.\n", blockstart, blockmax);
2192 : exit(EXIT_FAILURE);
2193 : /* LCOV_EXCL_STOP */
2194 : }
2195 :
2196 : /* adjust the number of block to process */
2197 134 : if (blockcount != 0 && blockstart + blockcount < blockmax) {
2198 0 : blockmax = blockstart + blockcount;
2199 : }
2200 :
2201 134 : if (fix) {
2202 : /*
2203 : * If fixing, create the file and open for writing
2204 : * if it fails, we cannot continue
2205 : */
2206 196 : for (l = 0; l < state->level; ++l) {
2207 : /* skip parity disks that are not accessible */
2208 145 : if (state->parity[l].skip_access) {
2209 1 : parity_ptr[l] = 0;
2210 1 : continue;
2211 : }
2212 :
2213 144 : parity_ptr[l] = &parity[l];
2214 :
2215 : /* if the parity is excluded */
2216 144 : if (state->parity[l].is_excluded_by_filter) {
2217 : /* open for reading, and ignore error */
2218 18 : ret = parity_open(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
2219 18 : if (ret == -1) {
2220 0 : log_tag("parity_%s:%u:%s: Open error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
2221 0 : if (is_hw(errno)) {
2222 0 : log_fatal_errno(errno, lev_config_name(l));
2223 0 : exit(EXIT_FAILURE);
2224 : }
2225 :
2226 : /* continue anyway */
2227 0 : parity_ptr[l] = 0;
2228 : }
2229 : } else {
2230 : /* open for writing */
2231 126 : ret = parity_create(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
2232 126 : if (ret == -1) {
2233 : /* LCOV_EXCL_START */
2234 : log_tag("parity_%s:%u:%s: Create error. %s.\n", es(errno), 0, lev_config_name(l), strerror(errno));
2235 : log_fatal_errno(errno, lev_config_name(l));
2236 : exit(EXIT_FAILURE);
2237 : /* LCOV_EXCL_STOP */
2238 : }
2239 :
2240 126 : ret = parity_chsize(parity_ptr[l], &state->parity[l], 0, size, state->block_size, state->opt.skip_fallocate, state->opt.skip_space_holder);
2241 126 : if (ret == -1) {
2242 : /* LCOV_EXCL_START */
2243 : log_tag("parity_%s:%u:%s: Create error. %s.\n", es(errno), 0, lev_config_name(l), strerror(errno));
2244 : log_fatal_errno(errno, lev_config_name(l));
2245 : exit(EXIT_FAILURE);
2246 : /* LCOV_EXCL_STOP */
2247 : }
2248 : }
2249 : }
2250 83 : } else if (!state->opt.auditonly) {
2251 : /*
2252 : * If checking, open the file for reading
2253 : * it may fail if the file doesn't exist, in this case we continue to check the files
2254 : */
2255 416 : for (l = 0; l < state->level; ++l) {
2256 339 : parity_ptr[l] = &parity[l];
2257 339 : ret = parity_open(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
2258 339 : if (ret == -1) {
2259 1 : log_tag("parity_%s:%u:%s: Open error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
2260 1 : if (is_hw(errno)) {
2261 0 : log_fatal_errno(errno, lev_config_name(l));
2262 0 : exit(EXIT_FAILURE);
2263 : }
2264 :
2265 1 : msg_status("No accessible %s file, only files will be checked.\n", lev_name(l));
2266 :
2267 : /* continue anyway */
2268 1 : parity_ptr[l] = 0;
2269 : }
2270 : }
2271 : } else {
2272 : /* otherwise don't use any parity */
2273 28 : for (l = 0; l < state->level; ++l)
2274 22 : parity_ptr[l] = 0;
2275 : }
2276 :
2277 134 : process_error = 0;
2278 :
2279 : /* skip degenerated cases of empty parity, or skipping all */
2280 134 : if (blockstart < blockmax) {
2281 132 : ret = state_check_process(state, fix, parity_ptr, blockstart, blockmax);
2282 132 : if (ret == -1) {
2283 : /* LCOV_EXCL_START */
2284 : ++process_error;
2285 : /* continue, as we are already exiting */
2286 : /* LCOV_EXCL_STOP */
2287 : }
2288 : }
2289 :
2290 : /* try to close only if opened */
2291 640 : for (l = 0; l < state->level; ++l) {
2292 506 : if (parity_ptr[l]) {
2293 : /* if fixing and not excluded, truncate parity not valid */
2294 482 : if (fix && !state->parity[l].is_excluded_by_filter) {
2295 126 : ret = parity_truncate(parity_ptr[l]);
2296 126 : if (ret == -1) {
2297 : /* LCOV_EXCL_START */
2298 : log_tag("parity_%s:%u:%s: Truncate error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
2299 : log_fatal_errno(errno, lev_config_name(l));
2300 :
2301 : ++process_error;
2302 : /* continue, as we are already exiting */
2303 : /* LCOV_EXCL_STOP */
2304 : }
2305 : }
2306 :
2307 482 : ret = parity_close(parity_ptr[l]);
2308 482 : if (ret == -1) {
2309 : /* LCOV_EXCL_START */
2310 : log_tag("parity_%s:%u:%s: Close error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
2311 : log_fatal_errno(errno, lev_config_name(l));
2312 :
2313 : ++process_error;
2314 : /* continue, as we are already exiting */
2315 : /* LCOV_EXCL_STOP */
2316 : }
2317 : }
2318 : }
2319 :
2320 134 : if (process_error != 0)
2321 0 : return -1;
2322 134 : return 0;
2323 : }
2324 :
|