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