1 /*
2 * Copyright (C) 2011 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 "support.h"
22 #include "util.h"
23
24 /****************************************************************************/
25 /* snapraid */
26
27 int BLOCK_HASH_SIZE = HASH_MAX;
28
content_alloc(const char * path,uint64_t dev)29 struct snapraid_content* content_alloc(const char* path, uint64_t dev)
30 {
31 struct snapraid_content* content;
32
33 content = malloc_nofail(sizeof(struct snapraid_content));
34 pathimport(content->content, sizeof(content->content), path);
35 content->device = dev;
36
37 return content;
38 }
39
content_free(struct snapraid_content * content)40 void content_free(struct snapraid_content* content)
41 {
42 free(content);
43 }
44
filter_alloc_file(int direction,const char * pattern)45 struct snapraid_filter* filter_alloc_file(int direction, const char* pattern)
46 {
47 struct snapraid_filter* filter;
48 char* i;
49 char* first;
50 char* last;
51 int token_is_valid;
52 int token_is_filled;
53
54 filter = malloc_nofail(sizeof(struct snapraid_filter));
55 pathimport(filter->pattern, sizeof(filter->pattern), pattern);
56 filter->direction = direction;
57
58 /* find first and last slash */
59 first = 0;
60 last = 0;
61 /* reject invalid tokens, like "<empty>", ".", ".." and more dots */
62 token_is_valid = 0;
63 token_is_filled = 0;
64 for (i = filter->pattern; *i; ++i) {
65 if (*i == '/') {
66 /* reject invalid tokens, but accept an empty one as first */
67 if (!token_is_valid && (first != 0 || token_is_filled)) {
68 free(filter);
69 return 0;
70 }
71 token_is_valid = 0;
72 token_is_filled = 0;
73
74 /* update slash position */
75 if (!first)
76 first = i;
77 last = i;
78 } else if (*i != '.') {
79 token_is_valid = 1;
80 token_is_filled = 1;
81 } else {
82 token_is_filled = 1;
83 }
84 }
85
86 /* reject invalid tokens, but accept an empty one as last, but not if it's the only one */
87 if (!token_is_valid && (first == 0 || token_is_filled)) {
88 free(filter);
89 return 0;
90 }
91
92 /* it's a file filter */
93 filter->is_disk = 0;
94
95 if (first == 0) {
96 /* no slash */
97 filter->is_path = 0;
98 filter->is_dir = 0;
99 } else if (first == last && last[1] == 0) {
100 /* one slash at the end */
101 filter->is_path = 0;
102 filter->is_dir = 1;
103 last[0] = 0;
104 } else {
105 /* at least a slash not at the end */
106 filter->is_path = 1;
107 if (last[1] == 0) {
108 filter->is_dir = 1;
109 last[0] = 0;
110 } else {
111 filter->is_dir = 0;
112 }
113
114 /* a slash must be the first char, as we don't support PATH/FILE and PATH/DIR/ */
115 if (filter->pattern[0] != '/') {
116 free(filter);
117 return 0;
118 }
119 }
120
121 return filter;
122 }
123
filter_alloc_disk(int direction,const char * pattern)124 struct snapraid_filter* filter_alloc_disk(int direction, const char* pattern)
125 {
126 struct snapraid_filter* filter;
127
128 filter = malloc_nofail(sizeof(struct snapraid_filter));
129 pathimport(filter->pattern, sizeof(filter->pattern), pattern);
130 filter->direction = direction;
131
132 /* it's a disk filter */
133 filter->is_disk = 1;
134 filter->is_path = 0;
135 filter->is_dir = 0;
136
137 /* no slash allowed in disk names */
138 if (strchr(filter->pattern, '/') != 0) {
139 /* LCOV_EXCL_START */
140 free(filter);
141 return 0;
142 /* LCOV_EXCL_STOP */
143 }
144
145 return filter;
146 }
147
filter_free(struct snapraid_filter * filter)148 void filter_free(struct snapraid_filter* filter)
149 {
150 free(filter);
151 }
152
filter_type(struct snapraid_filter * filter,char * out,size_t out_size)153 const char* filter_type(struct snapraid_filter* filter, char* out, size_t out_size)
154 {
155 const char* direction;
156
157 if (filter->direction < 0)
158 direction = "exclude";
159 else
160 direction = "include";
161
162 if (filter->is_disk)
163 pathprint(out, out_size, "%s %s:", direction, filter->pattern);
164 else if (filter->is_dir)
165 pathprint(out, out_size, "%s %s/", direction, filter->pattern);
166 else
167 pathprint(out, out_size, "%s %s", direction, filter->pattern);
168
169 return out;
170 }
171
filter_apply(struct snapraid_filter * filter,struct snapraid_filter ** reason,const char * path,const char * name,int is_dir)172 static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* path, const char* name, int is_dir)
173 {
174 int ret = 0;
175
176 /* match dirs with dirs and files with files */
177 if (filter->is_dir && !is_dir)
178 return 0;
179 if (!filter->is_dir && is_dir)
180 return 0;
181
182 if (filter->is_path) {
183 /* skip initial slash, as always missing from the path */
184 if (fnmatch(filter->pattern + 1, path, FNM_PATHNAME | FNM_CASEINSENSITIVE_FOR_WIN) == 0)
185 ret = filter->direction;
186 } else {
187 if (fnmatch(filter->pattern, name, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
188 ret = filter->direction;
189 }
190
191 if (reason != 0 && ret < 0)
192 *reason = filter;
193
194 return ret;
195 }
196
filter_recurse(struct snapraid_filter * filter,struct snapraid_filter ** reason,const char * const_path,int is_dir)197 static int filter_recurse(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* const_path, int is_dir)
198 {
199 char path[PATH_MAX];
200 char* name;
201 unsigned i;
202
203 pathcpy(path, sizeof(path), const_path);
204
205 /* filter for all the directories */
206 name = path;
207 for (i = 0; path[i] != 0; ++i) {
208 if (path[i] == '/') {
209 /* set a terminator */
210 path[i] = 0;
211
212 /* filter the directory */
213 if (filter_apply(filter, reason, path, name, 1) != 0)
214 return filter->direction;
215
216 /* restore the slash */
217 path[i] = '/';
218
219 /* next name */
220 name = path + i + 1;
221 }
222 }
223
224 /* filter the final file */
225 if (filter_apply(filter, reason, path, name, is_dir) != 0)
226 return filter->direction;
227
228 return 0;
229 }
230
filter_element(tommy_list * filterlist,struct snapraid_filter ** reason,const char * disk,const char * sub,int is_dir,int is_def_include)231 static int filter_element(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub, int is_dir, int is_def_include)
232 {
233 tommy_node* i;
234
235 int direction = 1; /* by default include all */
236
237 /* for each filter */
238 for (i = tommy_list_head(filterlist); i != 0; i = i->next) {
239 int ret;
240 struct snapraid_filter* filter = i->data;
241
242 if (filter->is_disk) {
243 if (fnmatch(filter->pattern, disk, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
244 ret = filter->direction;
245 else
246 ret = 0;
247 if (reason != 0 && ret < 0)
248 *reason = filter;
249 } else {
250 ret = filter_recurse(filter, reason, sub, is_dir);
251 }
252
253 if (ret > 0) {
254 /* include the file */
255 return 0;
256 } else if (ret < 0) {
257 /* exclude the file */
258 return -1;
259 } else {
260 /* default is opposite of the last filter */
261 direction = -filter->direction;
262 if (reason != 0 && direction < 0)
263 *reason = filter;
264 /* continue with the next one */
265 }
266 }
267
268 /* directories are always included by default, otherwise we cannot apply rules */
269 /* to the contained files */
270 if (is_def_include)
271 return 0;
272
273 /* files are excluded/included depending of the last rule processed */
274 if (direction < 0)
275 return -1;
276
277 return 0;
278 }
279
filter_path(tommy_list * filterlist,struct snapraid_filter ** reason,const char * disk,const char * sub)280 int filter_path(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub)
281 {
282 return filter_element(filterlist, reason, disk, sub, 0, 0);
283 }
284
filter_subdir(tommy_list * filterlist,struct snapraid_filter ** reason,const char * disk,const char * sub)285 int filter_subdir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub)
286 {
287 return filter_element(filterlist, reason, disk, sub, 1, 1);
288 }
289
filter_emptydir(tommy_list * filterlist,struct snapraid_filter ** reason,const char * disk,const char * sub)290 int filter_emptydir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub)
291 {
292 return filter_element(filterlist, reason, disk, sub, 1, 0);
293 }
294
filter_existence(int filter_missing,const char * dir,const char * sub)295 int filter_existence(int filter_missing, const char* dir, const char* sub)
296 {
297 char path[PATH_MAX];
298 struct stat st;
299
300 if (!filter_missing)
301 return 0;
302
303 /* we directly check if in the disk the file is present or not */
304 pathprint(path, sizeof(path), "%s%s", dir, sub);
305
306 if (lstat(path, &st) != 0) {
307 /* if the file doesn't exist, we don't filter it out */
308 if (errno == ENOENT)
309 return 0;
310 /* LCOV_EXCL_START */
311 log_fatal("Error in stat file '%s'. %s.\n", path, strerror(errno));
312 exit(EXIT_FAILURE);
313 /* LCOV_EXCL_STOP */
314 }
315
316 /* the file is present, so we filter it out */
317 return 1;
318 }
319
filter_correctness(int filter_error,tommy_arrayblkof * infoarr,struct snapraid_disk * disk,struct snapraid_file * file)320 int filter_correctness(int filter_error, tommy_arrayblkof* infoarr, struct snapraid_disk* disk, struct snapraid_file* file)
321 {
322 unsigned i;
323
324 if (!filter_error)
325 return 0;
326
327 /* check each block of the file */
328 for (i = 0; i < file->blockmax; ++i) {
329 block_off_t parity_pos = fs_file2par_get(disk, file, i);
330 snapraid_info info = info_get(infoarr, parity_pos);
331
332 /* if the file has a bad block, don't exclude it */
333 if (info_get_bad(info))
334 return 0;
335 }
336
337 /* the file is correct, so we filter it out */
338 return 1;
339 }
340
filter_content(tommy_list * contentlist,const char * path)341 int filter_content(tommy_list* contentlist, const char* path)
342 {
343 tommy_node* i;
344
345 for (i = tommy_list_head(contentlist); i != 0; i = i->next) {
346 struct snapraid_content* content = i->data;
347 char tmp[PATH_MAX];
348
349 if (pathcmp(content->content, path) == 0)
350 return -1;
351
352 /* exclude also the ".tmp" copy used to save it */
353 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
354 if (pathcmp(tmp, path) == 0)
355 return -1;
356
357 /* exclude also the ".lock" file */
358 pathprint(tmp, sizeof(tmp), "%s.lock", content->content);
359 if (pathcmp(tmp, path) == 0)
360 return -1;
361 }
362
363 return 0;
364 }
365
file_alloc(unsigned block_size,const char * sub,data_off_t size,uint64_t mtime_sec,int mtime_nsec,uint64_t inode,uint64_t physical)366 struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical)
367 {
368 struct snapraid_file* file;
369 block_off_t i;
370
371 file = malloc_nofail(sizeof(struct snapraid_file));
372 file->sub = strdup_nofail(sub);
373 file->size = size;
374 file->blockmax = (size + block_size - 1) / block_size;
375 file->mtime_sec = mtime_sec;
376 file->mtime_nsec = mtime_nsec;
377 file->inode = inode;
378 file->physical = physical;
379 file->flag = 0;
380 file->blockvec = malloc_nofail(file->blockmax * block_sizeof());
381
382 for (i = 0; i < file->blockmax; ++i) {
383 struct snapraid_block* block = file_block(file, i);
384 block_state_set(block, BLOCK_STATE_CHG);
385 hash_invalid_set(block->hash);
386 }
387
388 return file;
389 }
390
file_dup(struct snapraid_file * copy)391 struct snapraid_file* file_dup(struct snapraid_file* copy)
392 {
393 struct snapraid_file* file;
394 block_off_t i;
395
396 file = malloc_nofail(sizeof(struct snapraid_file));
397 file->sub = strdup_nofail(copy->sub);
398 file->size = copy->size;
399 file->blockmax = copy->blockmax;
400 file->mtime_sec = copy->mtime_sec;
401 file->mtime_nsec = copy->mtime_nsec;
402 file->inode = copy->inode;
403 file->physical = copy->physical;
404 file->flag = copy->flag;
405 file->blockvec = malloc_nofail(file->blockmax * block_sizeof());
406
407 for (i = 0; i < file->blockmax; ++i) {
408 struct snapraid_block* block = file_block(file, i);
409 struct snapraid_block* copy_block = file_block(copy, i);
410 block->state = copy_block->state;
411 memcpy(block->hash, copy_block->hash, BLOCK_HASH_SIZE);
412 }
413
414 return file;
415 }
416
file_free(struct snapraid_file * file)417 void file_free(struct snapraid_file* file)
418 {
419 free(file->sub);
420 file->sub = 0;
421 free(file->blockvec);
422 file->blockvec = 0;
423 free(file);
424 }
425
file_rename(struct snapraid_file * file,const char * sub)426 void file_rename(struct snapraid_file* file, const char* sub)
427 {
428 free(file->sub);
429 file->sub = strdup_nofail(sub);
430 }
431
file_copy(struct snapraid_file * src_file,struct snapraid_file * dst_file)432 void file_copy(struct snapraid_file* src_file, struct snapraid_file* dst_file)
433 {
434 block_off_t i;
435
436 if (src_file->size != dst_file->size) {
437 /* LCOV_EXCL_START */
438 log_fatal("Internal inconsistency in copy file with different size\n");
439 os_abort();
440 /* LCOV_EXCL_STOP */
441 }
442
443 if (src_file->mtime_sec != dst_file->mtime_sec) {
444 /* LCOV_EXCL_START */
445 log_fatal("Internal inconsistency in copy file with different mtime_sec\n");
446 os_abort();
447 /* LCOV_EXCL_STOP */
448 }
449
450 if (src_file->mtime_nsec != dst_file->mtime_nsec) {
451 /* LCOV_EXCL_START */
452 log_fatal("Internal inconsistency in copy file with different mtime_nsec\n");
453 os_abort();
454 /* LCOV_EXCL_STOP */
455 }
456
457 for (i = 0; i < dst_file->blockmax; ++i) {
458 /* set a block with hash computed but without parity */
459 block_state_set(file_block(dst_file, i), BLOCK_STATE_REP);
460
461 /* copy the hash */
462 memcpy(file_block(dst_file, i)->hash, file_block(src_file, i)->hash, BLOCK_HASH_SIZE);
463 }
464
465 file_flag_set(dst_file, FILE_IS_COPY);
466 }
467
file_name(const struct snapraid_file * file)468 const char* file_name(const struct snapraid_file* file)
469 {
470 const char* r = strrchr(file->sub, '/');
471
472 if (!r)
473 r = file->sub;
474 else
475 ++r;
476 return r;
477 }
478
file_block_size(struct snapraid_file * file,block_off_t file_pos,unsigned block_size)479 unsigned file_block_size(struct snapraid_file* file, block_off_t file_pos, unsigned block_size)
480 {
481 /* if it's the last block */
482 if (file_pos + 1 == file->blockmax) {
483 unsigned block_remainder;
484 if (file->size == 0)
485 return 0;
486 block_remainder = file->size % block_size;
487 if (block_remainder == 0)
488 block_remainder = block_size;
489 return block_remainder;
490 }
491
492 return block_size;
493 }
494
file_block_is_last(struct snapraid_file * file,block_off_t file_pos)495 int file_block_is_last(struct snapraid_file* file, block_off_t file_pos)
496 {
497 if (file_pos == 0 && file->blockmax == 0)
498 return 1;
499
500 if (file_pos >= file->blockmax) {
501 /* LCOV_EXCL_START */
502 log_fatal("Internal inconsistency in file block position\n");
503 os_abort();
504 /* LCOV_EXCL_STOP */
505 }
506
507 return file_pos == file->blockmax - 1;
508 }
509
file_inode_compare_to_arg(const void * void_arg,const void * void_data)510 int file_inode_compare_to_arg(const void* void_arg, const void* void_data)
511 {
512 const uint64_t* arg = void_arg;
513 const struct snapraid_file* file = void_data;
514
515 if (*arg < file->inode)
516 return -1;
517 if (*arg > file->inode)
518 return 1;
519 return 0;
520 }
521
file_inode_compare(const void * void_a,const void * void_b)522 int file_inode_compare(const void* void_a, const void* void_b)
523 {
524 const struct snapraid_file* file_a = void_a;
525 const struct snapraid_file* file_b = void_b;
526
527 if (file_a->inode < file_b->inode)
528 return -1;
529 if (file_a->inode > file_b->inode)
530 return 1;
531 return 0;
532 }
533
file_path_compare(const void * void_a,const void * void_b)534 int file_path_compare(const void* void_a, const void* void_b)
535 {
536 const struct snapraid_file* file_a = void_a;
537 const struct snapraid_file* file_b = void_b;
538
539 return strcmp(file_a->sub, file_b->sub);
540 }
541
file_physical_compare(const void * void_a,const void * void_b)542 int file_physical_compare(const void* void_a, const void* void_b)
543 {
544 const struct snapraid_file* file_a = void_a;
545 const struct snapraid_file* file_b = void_b;
546
547 if (file_a->physical < file_b->physical)
548 return -1;
549 if (file_a->physical > file_b->physical)
550 return 1;
551 return 0;
552 }
553
file_path_compare_to_arg(const void * void_arg,const void * void_data)554 int file_path_compare_to_arg(const void* void_arg, const void* void_data)
555 {
556 const char* arg = void_arg;
557 const struct snapraid_file* file = void_data;
558
559 return strcmp(arg, file->sub);
560 }
561
file_name_compare(const void * void_a,const void * void_b)562 int file_name_compare(const void* void_a, const void* void_b)
563 {
564 const struct snapraid_file* file_a = void_a;
565 const struct snapraid_file* file_b = void_b;
566 const char* name_a = file_name(file_a);
567 const char* name_b = file_name(file_b);
568
569 return strcmp(name_a, name_b);
570 }
571
file_stamp_compare(const void * void_a,const void * void_b)572 int file_stamp_compare(const void* void_a, const void* void_b)
573 {
574 const struct snapraid_file* file_a = void_a;
575 const struct snapraid_file* file_b = void_b;
576
577 if (file_a->size < file_b->size)
578 return -1;
579 if (file_a->size > file_b->size)
580 return 1;
581
582 if (file_a->mtime_sec < file_b->mtime_sec)
583 return -1;
584 if (file_a->mtime_sec > file_b->mtime_sec)
585 return 1;
586
587 if (file_a->mtime_nsec < file_b->mtime_nsec)
588 return -1;
589 if (file_a->mtime_nsec > file_b->mtime_nsec)
590 return 1;
591
592 return 0;
593 }
594
file_namestamp_compare(const void * void_a,const void * void_b)595 int file_namestamp_compare(const void* void_a, const void* void_b)
596 {
597 int ret;
598
599 ret = file_name_compare(void_a, void_b);
600 if (ret != 0)
601 return ret;
602
603 return file_stamp_compare(void_a, void_b);
604 }
605
file_pathstamp_compare(const void * void_a,const void * void_b)606 int file_pathstamp_compare(const void* void_a, const void* void_b)
607 {
608 int ret;
609
610 ret = file_path_compare(void_a, void_b);
611 if (ret != 0)
612 return ret;
613
614 return file_stamp_compare(void_a, void_b);
615 }
616
extent_alloc(block_off_t parity_pos,struct snapraid_file * file,block_off_t file_pos,block_off_t count)617 struct snapraid_extent* extent_alloc(block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count)
618 {
619 struct snapraid_extent* extent;
620
621 if (count == 0) {
622 /* LCOV_EXCL_START */
623 log_fatal("Internal inconsistency when allocating empty extent for file '%s' at position '%u/%u'\n", file->sub, file_pos, file->blockmax);
624 os_abort();
625 /* LCOV_EXCL_STOP */
626 }
627 if (file_pos + count > file->blockmax) {
628 /* LCOV_EXCL_START */
629 log_fatal("Internal inconsistency when allocating overflowing extent for file '%s' at position '%u:%u/%u'\n", file->sub, file_pos, count, file->blockmax);
630 os_abort();
631 /* LCOV_EXCL_STOP */
632 }
633
634 extent = malloc_nofail(sizeof(struct snapraid_extent));
635 extent->parity_pos = parity_pos;
636 extent->file = file;
637 extent->file_pos = file_pos;
638 extent->count = count;
639
640 return extent;
641 }
642
extent_free(struct snapraid_extent * extent)643 void extent_free(struct snapraid_extent* extent)
644 {
645 free(extent);
646 }
647
extent_parity_compare(const void * void_a,const void * void_b)648 int extent_parity_compare(const void* void_a, const void* void_b)
649 {
650 const struct snapraid_extent* arg_a = void_a;
651 const struct snapraid_extent* arg_b = void_b;
652
653 if (arg_a->parity_pos < arg_b->parity_pos)
654 return -1;
655 if (arg_a->parity_pos > arg_b->parity_pos)
656 return 1;
657
658 return 0;
659 }
660
extent_file_compare(const void * void_a,const void * void_b)661 int extent_file_compare(const void* void_a, const void* void_b)
662 {
663 const struct snapraid_extent* arg_a = void_a;
664 const struct snapraid_extent* arg_b = void_b;
665
666 if (arg_a->file < arg_b->file)
667 return -1;
668 if (arg_a->file > arg_b->file)
669 return 1;
670
671 if (arg_a->file_pos < arg_b->file_pos)
672 return -1;
673 if (arg_a->file_pos > arg_b->file_pos)
674 return 1;
675
676 return 0;
677 }
678
link_alloc(const char * sub,const char * linkto,unsigned link_flag)679 struct snapraid_link* link_alloc(const char* sub, const char* linkto, unsigned link_flag)
680 {
681 struct snapraid_link* slink;
682
683 slink = malloc_nofail(sizeof(struct snapraid_link));
684 slink->sub = strdup_nofail(sub);
685 slink->linkto = strdup_nofail(linkto);
686 slink->flag = link_flag;
687
688 return slink;
689 }
690
link_free(struct snapraid_link * slink)691 void link_free(struct snapraid_link* slink)
692 {
693 free(slink->sub);
694 free(slink->linkto);
695 free(slink);
696 }
697
link_name_compare_to_arg(const void * void_arg,const void * void_data)698 int link_name_compare_to_arg(const void* void_arg, const void* void_data)
699 {
700 const char* arg = void_arg;
701 const struct snapraid_link* slink = void_data;
702
703 return strcmp(arg, slink->sub);
704 }
705
link_alpha_compare(const void * void_a,const void * void_b)706 int link_alpha_compare(const void* void_a, const void* void_b)
707 {
708 const struct snapraid_link* slink_a = void_a;
709 const struct snapraid_link* slink_b = void_b;
710
711 return strcmp(slink_a->sub, slink_b->sub);
712 }
713
dir_alloc(const char * sub)714 struct snapraid_dir* dir_alloc(const char* sub)
715 {
716 struct snapraid_dir* dir;
717
718 dir = malloc_nofail(sizeof(struct snapraid_dir));
719 dir->sub = strdup_nofail(sub);
720 dir->flag = 0;
721
722 return dir;
723 }
724
dir_free(struct snapraid_dir * dir)725 void dir_free(struct snapraid_dir* dir)
726 {
727 free(dir->sub);
728 free(dir);
729 }
730
dir_name_compare(const void * void_arg,const void * void_data)731 int dir_name_compare(const void* void_arg, const void* void_data)
732 {
733 const char* arg = void_arg;
734 const struct snapraid_dir* dir = void_data;
735
736 return strcmp(arg, dir->sub);
737 }
738
disk_alloc(const char * name,const char * dir,uint64_t dev,const char * uuid,int skip_access)739 struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev, const char* uuid, int skip_access)
740 {
741 struct snapraid_disk* disk;
742
743 disk = malloc_nofail(sizeof(struct snapraid_disk));
744 pathcpy(disk->name, sizeof(disk->name), name);
745 pathimport(disk->dir, sizeof(disk->dir), dir);
746 pathcpy(disk->uuid, sizeof(disk->uuid), uuid);
747
748 /* ensure that the dir terminate with "/" if it isn't empty */
749 pathslash(disk->dir, sizeof(disk->dir));
750
751 #if HAVE_THREAD
752 thread_mutex_init(&disk->fs_mutex);
753 disk->fs_mutex_enabled = 0; /* lock will be enabled at threads start */
754 #endif
755
756 disk->smartctl[0] = 0;
757 disk->device = dev;
758 disk->tick = 0;
759 disk->cached_blocks = 0;
760 disk->progress_file = 0;
761 disk->total_blocks = 0;
762 disk->free_blocks = 0;
763 disk->first_free_block = 0;
764 disk->has_volatile_inodes = 0;
765 disk->has_volatile_hardlinks = 0;
766 disk->has_unreliable_physical = 0;
767 disk->has_different_uuid = 0;
768 disk->has_unsupported_uuid = *uuid == 0; /* empty UUID means unsupported */
769 disk->had_empty_uuid = 0;
770 disk->mapping_idx = -1;
771 disk->skip_access = skip_access;
772 tommy_list_init(&disk->filelist);
773 tommy_list_init(&disk->deletedlist);
774 tommy_hashdyn_init(&disk->inodeset);
775 tommy_hashdyn_init(&disk->pathset);
776 tommy_hashdyn_init(&disk->stampset);
777 tommy_list_init(&disk->linklist);
778 tommy_hashdyn_init(&disk->linkset);
779 tommy_list_init(&disk->dirlist);
780 tommy_hashdyn_init(&disk->dirset);
781 tommy_tree_init(&disk->fs_parity, extent_parity_compare);
782 tommy_tree_init(&disk->fs_file, extent_file_compare);
783 disk->fs_last = 0;
784
785 return disk;
786 }
787
disk_free(struct snapraid_disk * disk)788 void disk_free(struct snapraid_disk* disk)
789 {
790 tommy_list_foreach(&disk->filelist, (tommy_foreach_func*)file_free);
791 tommy_list_foreach(&disk->deletedlist, (tommy_foreach_func*)file_free);
792 tommy_tree_foreach(&disk->fs_file, (tommy_foreach_func*)extent_free);
793 tommy_hashdyn_done(&disk->inodeset);
794 tommy_hashdyn_done(&disk->pathset);
795 tommy_hashdyn_done(&disk->stampset);
796 tommy_list_foreach(&disk->linklist, (tommy_foreach_func*)link_free);
797 tommy_hashdyn_done(&disk->linkset);
798 tommy_list_foreach(&disk->dirlist, (tommy_foreach_func*)dir_free);
799 tommy_hashdyn_done(&disk->dirset);
800
801 #if HAVE_THREAD
802 thread_mutex_destroy(&disk->fs_mutex);
803 #endif
804
805 free(disk);
806 }
807
disk_start_thread(struct snapraid_disk * disk)808 void disk_start_thread(struct snapraid_disk* disk)
809 {
810 #if HAVE_THREAD
811 disk->fs_mutex_enabled = 1;
812 #else
813 (void)disk;
814 #endif
815 }
816
fs_lock(struct snapraid_disk * disk)817 static inline void fs_lock(struct snapraid_disk* disk)
818 {
819 #if HAVE_THREAD
820 if (disk->fs_mutex_enabled)
821 thread_mutex_lock(&disk->fs_mutex);
822 #else
823 (void)disk;
824 #endif
825 }
826
fs_unlock(struct snapraid_disk * disk)827 static inline void fs_unlock(struct snapraid_disk* disk)
828 {
829 #if HAVE_THREAD
830 if (disk->fs_mutex_enabled)
831 thread_mutex_unlock(&disk->fs_mutex);
832 #else
833 (void)disk;
834 #endif
835 }
836
837 struct extent_disk_empty {
838 block_off_t blockmax;
839 };
840
841 /**
842 * Compare the extent if inside the specified blockmax.
843 */
extent_disk_empty_compare_unlock(const void * void_a,const void * void_b)844 static int extent_disk_empty_compare_unlock(const void* void_a, const void* void_b)
845 {
846 const struct extent_disk_empty* arg_a = void_a;
847 const struct snapraid_extent* arg_b = void_b;
848
849 /* if the block is inside the specified blockmax, it's found */
850 if (arg_a->blockmax > arg_b->parity_pos)
851 return 0;
852
853 /* otherwise search for a smaller one */
854 return -1;
855 }
856
fs_is_empty(struct snapraid_disk * disk,block_off_t blockmax)857 int fs_is_empty(struct snapraid_disk* disk, block_off_t blockmax)
858 {
859 struct extent_disk_empty arg = { blockmax };
860
861 /* if there is an element, it's not empty */
862 /* even if links and dirs have no block allocation */
863 if (!tommy_list_empty(&disk->filelist))
864 return 0;
865 if (!tommy_list_empty(&disk->linklist))
866 return 0;
867 if (!tommy_list_empty(&disk->dirlist))
868 return 0;
869
870 fs_lock(disk);
871
872 /* search for any extent inside blockmax */
873 if (tommy_tree_search_compare(&disk->fs_parity, extent_disk_empty_compare_unlock, &arg) != 0) {
874 fs_unlock(disk);
875 return 0;
876 }
877
878 /* finally, it's empty */
879 fs_unlock(disk);
880 return 1;
881 }
882
883 struct extent_disk_size {
884 block_off_t size;
885 };
886
887 /**
888 * Compare the extent by highest parity position.
889 *
890 * The maximum parity position is stored as size.
891 */
extent_disk_size_compare_unlock(const void * void_a,const void * void_b)892 static int extent_disk_size_compare_unlock(const void* void_a, const void* void_b)
893 {
894 struct extent_disk_size* arg_a = (void*)void_a;
895 const struct snapraid_extent* arg_b = void_b;
896
897 /* get the maximum size */
898 if (arg_a->size < arg_b->parity_pos + arg_b->count)
899 arg_a->size = arg_b->parity_pos + arg_b->count;
900
901 /* search always for a bigger one */
902 return 1;
903 }
904
fs_size(struct snapraid_disk * disk)905 block_off_t fs_size(struct snapraid_disk* disk)
906 {
907 struct extent_disk_size arg = { 0 };
908
909 fs_lock(disk);
910
911 tommy_tree_search_compare(&disk->fs_parity, extent_disk_size_compare_unlock, &arg);
912
913 fs_unlock(disk);
914
915 return arg.size;
916 }
917
918 struct extent_check {
919 const struct snapraid_extent* prev;
920 int result;
921 };
922
extent_parity_check_foreach_unlock(void * void_arg,void * void_obj)923 static void extent_parity_check_foreach_unlock(void* void_arg, void* void_obj)
924 {
925 struct extent_check* arg = void_arg;
926 const struct snapraid_extent* obj = void_obj;
927 const struct snapraid_extent* prev = arg->prev;
928
929 /* set the next previous block */
930 arg->prev = obj;
931
932 /* stop reporting if too many errors */
933 if (arg->result > 100) {
934 /* LCOV_EXCL_START */
935 return;
936 /* LCOV_EXCL_STOP */
937 }
938
939 if (obj->count == 0) {
940 /* LCOV_EXCL_START */
941 log_fatal("Internal inconsistency in parity count zero for file '%s' at '%u'\n",
942 obj->file->sub, obj->parity_pos);
943 ++arg->result;
944 return;
945 /* LCOV_EXCL_STOP */
946 }
947
948 /* check only if there is a previous block */
949 if (!prev)
950 return;
951
952 /* check the order */
953 if (prev->parity_pos >= obj->parity_pos) {
954 /* LCOV_EXCL_START */
955 log_fatal("Internal inconsistency in parity order for files '%s' at '%u:%u' and '%s' at '%u:%u'\n",
956 prev->file->sub, prev->parity_pos, prev->count, obj->file->sub, obj->parity_pos, obj->count);
957 ++arg->result;
958 return;
959 /* LCOV_EXCL_STOP */
960 }
961
962 /* check that the extents don't overlap */
963 if (prev->parity_pos + prev->count > obj->parity_pos) {
964 /* LCOV_EXCL_START */
965 log_fatal("Internal inconsistency for parity overlap for files '%s' at '%u:%u' and '%s' at '%u:%u'\n",
966 prev->file->sub, prev->parity_pos, prev->count, obj->file->sub, obj->parity_pos, obj->count);
967 ++arg->result;
968 return;
969 /* LCOV_EXCL_STOP */
970 }
971 }
972
extent_file_check_foreach_unlock(void * void_arg,void * void_obj)973 static void extent_file_check_foreach_unlock(void* void_arg, void* void_obj)
974 {
975 struct extent_check* arg = void_arg;
976 const struct snapraid_extent* obj = void_obj;
977 const struct snapraid_extent* prev = arg->prev;
978
979 /* set the next previous block */
980 arg->prev = obj;
981
982 /* stop reporting if too many errors */
983 if (arg->result > 100) {
984 /* LCOV_EXCL_START */
985 return;
986 /* LCOV_EXCL_STOP */
987 }
988
989 if (obj->count == 0) {
990 /* LCOV_EXCL_START */
991 log_fatal("Internal inconsistency in file count zero for file '%s' at '%u'\n",
992 obj->file->sub, obj->file_pos);
993 ++arg->result;
994 return;
995 /* LCOV_EXCL_STOP */
996 }
997
998 /* note that for deleted files, some extents may be missing */
999
1000 /* if the files are different */
1001 if (!prev || prev->file != obj->file) {
1002 if (prev != 0) {
1003 if (file_flag_has(prev->file, FILE_IS_DELETED)) {
1004 /* check that the extent doesn't overflow the file */
1005 if (prev->file_pos + prev->count > prev->file->blockmax) {
1006 /* LCOV_EXCL_START */
1007 log_fatal("Internal inconsistency in delete end for file '%s' at '%u:%u' overflowing size '%u'\n",
1008 prev->file->sub, prev->file_pos, prev->count, prev->file->blockmax);
1009 ++arg->result;
1010 return;
1011 /* LCOV_EXCL_STOP */
1012 }
1013 } else {
1014 /* check that the extent ends the file */
1015 if (prev->file_pos + prev->count != prev->file->blockmax) {
1016 /* LCOV_EXCL_START */
1017 log_fatal("Internal inconsistency in file end for file '%s' at '%u:%u' instead of size '%u'\n",
1018 prev->file->sub, prev->file_pos, prev->count, prev->file->blockmax);
1019 ++arg->result;
1020 return;
1021 /* LCOV_EXCL_STOP */
1022 }
1023 }
1024 }
1025
1026 if (file_flag_has(obj->file, FILE_IS_DELETED)) {
1027 /* check that the extent doesn't overflow the file */
1028 if (obj->file_pos + obj->count > obj->file->blockmax) {
1029 /* LCOV_EXCL_START */
1030 log_fatal("Internal inconsistency in delete start for file '%s' at '%u:%u' overflowing size '%u'\n",
1031 obj->file->sub, obj->file_pos, obj->count, obj->file->blockmax);
1032 ++arg->result;
1033 return;
1034 /* LCOV_EXCL_STOP */
1035 }
1036 } else {
1037 /* check that the extent starts the file */
1038 if (obj->file_pos != 0) {
1039 /* LCOV_EXCL_START */
1040 log_fatal("Internal inconsistency in file start for file '%s' at '%u:%u'\n",
1041 obj->file->sub, obj->file_pos, obj->count);
1042 ++arg->result;
1043 return;
1044 /* LCOV_EXCL_STOP */
1045 }
1046 }
1047 } else {
1048 /* check the order */
1049 if (prev->file_pos >= obj->file_pos) {
1050 /* LCOV_EXCL_START */
1051 log_fatal("Internal inconsistency in file order for file '%s' at '%u:%u' and at '%u:%u'\n",
1052 prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
1053 ++arg->result;
1054 return;
1055 /* LCOV_EXCL_STOP */
1056 }
1057
1058 if (file_flag_has(obj->file, FILE_IS_DELETED)) {
1059 /* check that the extents don't overlap */
1060 if (prev->file_pos + prev->count > obj->file_pos) {
1061 /* LCOV_EXCL_START */
1062 log_fatal("Internal inconsistency in delete sequence for file '%s' at '%u:%u' and at '%u:%u'\n",
1063 prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
1064 ++arg->result;
1065 return;
1066 /* LCOV_EXCL_STOP */
1067 }
1068 } else {
1069 /* check that the extents are sequential */
1070 if (prev->file_pos + prev->count != obj->file_pos) {
1071 /* LCOV_EXCL_START */
1072 log_fatal("Internal inconsistency in file sequence for file '%s' at '%u:%u' and at '%u:%u'\n",
1073 prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
1074 ++arg->result;
1075 return;
1076 /* LCOV_EXCL_STOP */
1077 }
1078 }
1079 }
1080 }
1081
fs_check(struct snapraid_disk * disk)1082 int fs_check(struct snapraid_disk* disk)
1083 {
1084 struct extent_check arg;
1085
1086 /* error count starts from 0 */
1087 arg.result = 0;
1088
1089 fs_lock(disk);
1090
1091 /* check parity sequence */
1092 arg.prev = 0;
1093 tommy_tree_foreach_arg(&disk->fs_parity, extent_parity_check_foreach_unlock, &arg);
1094
1095 /* check file sequence */
1096 arg.prev = 0;
1097 tommy_tree_foreach_arg(&disk->fs_file, extent_file_check_foreach_unlock, &arg);
1098
1099 fs_unlock(disk);
1100
1101 if (arg.result != 0)
1102 return -1;
1103
1104 return 0;
1105 }
1106
1107 struct extent_parity_inside {
1108 block_off_t parity_pos;
1109 };
1110
1111 /**
1112 * Compare the extent if containing the specified parity position.
1113 */
extent_parity_inside_compare_unlock(const void * void_a,const void * void_b)1114 static int extent_parity_inside_compare_unlock(const void* void_a, const void* void_b)
1115 {
1116 const struct extent_parity_inside* arg_a = void_a;
1117 const struct snapraid_extent* arg_b = void_b;
1118
1119 if (arg_a->parity_pos < arg_b->parity_pos)
1120 return -1;
1121 if (arg_a->parity_pos >= arg_b->parity_pos + arg_b->count)
1122 return 1;
1123
1124 return 0;
1125 }
1126
1127 /**
1128 * Search the extent at the specified parity position.
1129 * The search is optimized for sequential accesses.
1130 * \return If not found return 0
1131 */
fs_par2extent_get_unlock(struct snapraid_disk * disk,struct snapraid_extent ** fs_last,block_off_t parity_pos)1132 static struct snapraid_extent* fs_par2extent_get_unlock(struct snapraid_disk* disk, struct snapraid_extent** fs_last, block_off_t parity_pos)
1133 {
1134 struct snapraid_extent* extent;
1135
1136 /* check if the last accessed extent matches */
1137 if (*fs_last
1138 && parity_pos >= (*fs_last)->parity_pos
1139 && parity_pos < (*fs_last)->parity_pos + (*fs_last)->count
1140 ) {
1141 extent = *fs_last;
1142 } else {
1143 struct extent_parity_inside arg = { parity_pos };
1144 extent = tommy_tree_search_compare(&disk->fs_parity, extent_parity_inside_compare_unlock, &arg);
1145 }
1146
1147 if (!extent)
1148 return 0;
1149
1150 /* store the last accessed extent */
1151 *fs_last = extent;
1152
1153 return extent;
1154 }
1155
1156 struct extent_file_inside {
1157 struct snapraid_file* file;
1158 block_off_t file_pos;
1159 };
1160
1161 /**
1162 * Compare the extent if containing the specified file position.
1163 */
extent_file_inside_compare_unlock(const void * void_a,const void * void_b)1164 static int extent_file_inside_compare_unlock(const void* void_a, const void* void_b)
1165 {
1166 const struct extent_file_inside* arg_a = void_a;
1167 const struct snapraid_extent* arg_b = void_b;
1168
1169 if (arg_a->file < arg_b->file)
1170 return -1;
1171 if (arg_a->file > arg_b->file)
1172 return 1;
1173
1174 if (arg_a->file_pos < arg_b->file_pos)
1175 return -1;
1176 if (arg_a->file_pos >= arg_b->file_pos + arg_b->count)
1177 return 1;
1178
1179 return 0;
1180 }
1181
1182 /**
1183 * Search the extent at the specified file position.
1184 * The search is optimized for sequential accesses.
1185 * \return If not found return 0
1186 */
fs_file2extent_get_unlock(struct snapraid_disk * disk,struct snapraid_extent ** fs_last,struct snapraid_file * file,block_off_t file_pos)1187 static struct snapraid_extent* fs_file2extent_get_unlock(struct snapraid_disk* disk, struct snapraid_extent** fs_last, struct snapraid_file* file, block_off_t file_pos)
1188 {
1189 struct snapraid_extent* extent;
1190
1191 /* check if the last accessed extent matches */
1192 if (*fs_last
1193 && file == (*fs_last)->file
1194 && file_pos >= (*fs_last)->file_pos
1195 && file_pos < (*fs_last)->file_pos + (*fs_last)->count
1196 ) {
1197 extent = *fs_last;
1198 } else {
1199 struct extent_file_inside arg = { file, file_pos };
1200 extent = tommy_tree_search_compare(&disk->fs_file, extent_file_inside_compare_unlock, &arg);
1201 }
1202
1203 if (!extent)
1204 return 0;
1205
1206 /* store the last accessed extent */
1207 *fs_last = extent;
1208
1209 return extent;
1210 }
1211
fs_par2file_find(struct snapraid_disk * disk,block_off_t parity_pos,block_off_t * file_pos)1212 struct snapraid_file* fs_par2file_find(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
1213 {
1214 struct snapraid_extent* extent;
1215 struct snapraid_file* file;
1216
1217 fs_lock(disk);
1218
1219 extent = fs_par2extent_get_unlock(disk, &disk->fs_last, parity_pos);
1220
1221 if (!extent) {
1222 fs_unlock(disk);
1223 return 0;
1224 }
1225
1226 if (file_pos)
1227 *file_pos = extent->file_pos + (parity_pos - extent->parity_pos);
1228
1229 file = extent->file;
1230
1231 fs_unlock(disk);
1232 return file;
1233 }
1234
fs_file2par_find(struct snapraid_disk * disk,struct snapraid_file * file,block_off_t file_pos)1235 block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
1236 {
1237 struct snapraid_extent* extent;
1238 block_off_t ret;
1239
1240 fs_lock(disk);
1241
1242 extent = fs_file2extent_get_unlock(disk, &disk->fs_last, file, file_pos);
1243 if (!extent) {
1244 fs_unlock(disk);
1245 return POS_NULL;
1246 }
1247
1248 ret = extent->parity_pos + (file_pos - extent->file_pos);
1249
1250 fs_unlock(disk);
1251 return ret;
1252 }
1253
fs_allocate(struct snapraid_disk * disk,block_off_t parity_pos,struct snapraid_file * file,block_off_t file_pos)1254 void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos)
1255 {
1256 struct snapraid_extent* extent;
1257 struct snapraid_extent* parity_extent;
1258 struct snapraid_extent* file_extent;
1259
1260 fs_lock(disk);
1261
1262 if (file_pos > 0) {
1263 /* search an existing extent for the previous file_pos */
1264 extent = fs_file2extent_get_unlock(disk, &disk->fs_last, file, file_pos - 1);
1265
1266 if (extent != 0 && parity_pos == extent->parity_pos + extent->count) {
1267 /* ensure that we are extending the extent at the end */
1268 if (file_pos != extent->file_pos + extent->count) {
1269 /* LCOV_EXCL_START */
1270 log_fatal("Internal inconsistency when allocating file '%s' at position '%u/%u' in the middle of extent '%u:%u' in disk '%s'\n", file->sub, file_pos, file->blockmax, extent->file_pos, extent->count, disk->name);
1271 os_abort();
1272 /* LCOV_EXCL_STOP */
1273 }
1274
1275 /* extend the existing extent */
1276 ++extent->count;
1277
1278 fs_unlock(disk);
1279 return;
1280 }
1281 }
1282
1283 /* a extent doesn't exist, and we have to create a new one */
1284 extent = extent_alloc(parity_pos, file, file_pos, 1);
1285
1286 /* insert the extent in the trees */
1287 parity_extent = tommy_tree_insert(&disk->fs_parity, &extent->parity_node, extent);
1288 file_extent = tommy_tree_insert(&disk->fs_file, &extent->file_node, extent);
1289
1290 if (parity_extent != extent || file_extent != extent) {
1291 /* LCOV_EXCL_START */
1292 log_fatal("Internal inconsistency when allocating file '%s' at position '%u/%u' for existing extent '%u:%u' in disk '%s'\n", file->sub, file_pos, file->blockmax, extent->file_pos, extent->count, disk->name);
1293 os_abort();
1294 /* LCOV_EXCL_STOP */
1295 }
1296
1297 /* store the last accessed extent */
1298 disk->fs_last = extent;
1299
1300 fs_unlock(disk);
1301 }
1302
fs_deallocate(struct snapraid_disk * disk,block_off_t parity_pos)1303 void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos)
1304 {
1305 struct snapraid_extent* extent;
1306 struct snapraid_extent* second_extent;
1307 struct snapraid_extent* parity_extent;
1308 struct snapraid_extent* file_extent;
1309 block_off_t first_count, second_count;
1310
1311 fs_lock(disk);
1312
1313 extent = fs_par2extent_get_unlock(disk, &disk->fs_last, parity_pos);
1314 if (!extent) {
1315 /* LCOV_EXCL_START */
1316 log_fatal("Internal inconsistency when deallocating parity position '%u' for not existing extent in disk '%s'\n", parity_pos, disk->name);
1317 os_abort();
1318 /* LCOV_EXCL_STOP */
1319 }
1320
1321 /* if it's the only block of the extent, delete it */
1322 if (extent->count == 1) {
1323 /* remove from the trees */
1324 tommy_tree_remove(&disk->fs_parity, extent);
1325 tommy_tree_remove(&disk->fs_file, extent);
1326
1327 /* deallocate */
1328 extent_free(extent);
1329
1330 /* clear the last accessed extent */
1331 disk->fs_last = 0;
1332
1333 fs_unlock(disk);
1334 return;
1335 }
1336
1337 /* if it's at the start of the extent, shrink the extent */
1338 if (parity_pos == extent->parity_pos) {
1339 ++extent->parity_pos;
1340 ++extent->file_pos;
1341 --extent->count;
1342
1343 fs_unlock(disk);
1344 return;
1345 }
1346
1347 /* if it's at the end of the extent, shrink the extent */
1348 if (parity_pos == extent->parity_pos + extent->count - 1) {
1349 --extent->count;
1350
1351 fs_unlock(disk);
1352 return;
1353 }
1354
1355 /* otherwise it's in the middle */
1356 first_count = parity_pos - extent->parity_pos;
1357 second_count = extent->count - first_count - 1;
1358
1359 /* adjust the first extent */
1360 extent->count = first_count;
1361
1362 /* allocate the second extent */
1363 second_extent = extent_alloc(extent->parity_pos + first_count + 1, extent->file, extent->file_pos + first_count + 1, second_count);
1364
1365 /* insert the extent in the trees */
1366 parity_extent = tommy_tree_insert(&disk->fs_parity, &second_extent->parity_node, second_extent);
1367 file_extent = tommy_tree_insert(&disk->fs_file, &second_extent->file_node, second_extent);
1368
1369 if (parity_extent != second_extent || file_extent != second_extent) {
1370 /* LCOV_EXCL_START */
1371 log_fatal("Internal inconsistency when deallocating parity position '%u' for splitting extent '%u:%u' in disk '%s'\n", parity_pos, second_extent->file_pos, second_extent->count, disk->name);
1372 os_abort();
1373 /* LCOV_EXCL_STOP */
1374 }
1375
1376 /* store the last accessed extent */
1377 disk->fs_last = second_extent;
1378
1379 fs_unlock(disk);
1380 }
1381
fs_file2block_get(struct snapraid_file * file,block_off_t file_pos)1382 struct snapraid_block* fs_file2block_get(struct snapraid_file* file, block_off_t file_pos)
1383 {
1384 if (file_pos >= file->blockmax) {
1385 /* LCOV_EXCL_START */
1386 log_fatal("Internal inconsistency when dereferencing file '%s' at position '%u/%u'\n", file->sub, file_pos, file->blockmax);
1387 os_abort();
1388 /* LCOV_EXCL_STOP */
1389 }
1390
1391 return file_block(file, file_pos);
1392 }
1393
fs_par2block_find(struct snapraid_disk * disk,block_off_t parity_pos)1394 struct snapraid_block* fs_par2block_find(struct snapraid_disk* disk, block_off_t parity_pos)
1395 {
1396 struct snapraid_file* file;
1397 block_off_t file_pos;
1398
1399 file = fs_par2file_find(disk, parity_pos, &file_pos);
1400 if (file == 0)
1401 return BLOCK_NULL;
1402
1403 return fs_file2block_get(file, file_pos);
1404 }
1405
map_alloc(const char * name,unsigned position,block_off_t total_blocks,block_off_t free_blocks,const char * uuid)1406 struct snapraid_map* map_alloc(const char* name, unsigned position, block_off_t total_blocks, block_off_t free_blocks, const char* uuid)
1407 {
1408 struct snapraid_map* map;
1409
1410 map = malloc_nofail(sizeof(struct snapraid_map));
1411 pathcpy(map->name, sizeof(map->name), name);
1412 map->position = position;
1413 map->total_blocks = total_blocks;
1414 map->free_blocks = free_blocks;
1415 pathcpy(map->uuid, sizeof(map->uuid), uuid);
1416
1417 return map;
1418 }
1419
map_free(struct snapraid_map * map)1420 void map_free(struct snapraid_map* map)
1421 {
1422 free(map);
1423 }
1424
time_compare(const void * void_a,const void * void_b)1425 int time_compare(const void* void_a, const void* void_b)
1426 {
1427 const time_t* time_a = void_a;
1428 const time_t* time_b = void_b;
1429
1430 if (*time_a < *time_b)
1431 return -1;
1432 if (*time_a > *time_b)
1433 return 1;
1434 return 0;
1435 }
1436
1437 /****************************************************************************/
1438 /* format */
1439
1440 int FMT_MODE = FMT_FILE;
1441
1442 /**
1443 * Format a file path for poll reference
1444 */
fmt_poll(const struct snapraid_disk * disk,const char * str,char * buffer)1445 const char* fmt_poll(const struct snapraid_disk* disk, const char* str, char* buffer)
1446 {
1447 (void)disk;
1448 return esc_shell(str, buffer);
1449 }
1450
1451 /**
1452 * Format a path name for terminal reference
1453 */
fmt_term(const struct snapraid_disk * disk,const char * str,char * buffer)1454 const char* fmt_term(const struct snapraid_disk* disk, const char* str, char* buffer)
1455 {
1456 const char* out[3];
1457
1458 switch (FMT_MODE) {
1459 case FMT_FILE :
1460 default :
1461 return esc_shell(str, buffer);
1462 case FMT_DISK :
1463 out[0] = disk->name;
1464 out[1] = ":";
1465 out[2] = str;
1466 return esc_shell_multi(out, 3, buffer);
1467 case FMT_PATH :
1468 out[0] = disk->dir;
1469 out[1] = str;
1470 return esc_shell_multi(out, 2, buffer);
1471 }
1472 }
1473
1474