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