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 1546 : struct snapraid_content* content_alloc(const char* path, uint64_t dev)
30 : {
31 : struct snapraid_content* content;
32 :
33 1546 : content = malloc_nofail(sizeof(struct snapraid_content));
34 1546 : pathimport(content->content, sizeof(content->content), path);
35 1546 : content->device = dev;
36 :
37 1546 : return content;
38 : }
39 :
40 1491 : void content_free(struct snapraid_content* content)
41 : {
42 1491 : free(content);
43 1491 : }
44 :
45 540 : 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 540 : filter = malloc_nofail(sizeof(struct snapraid_filter));
55 540 : pathimport(filter->pattern, sizeof(filter->pattern), pattern);
56 540 : filter->direction = direction;
57 :
58 : /* find first and last slash */
59 540 : first = 0;
60 540 : last = 0;
61 : /* reject invalid tokens, like "<empty>", ".", ".." and more dots */
62 540 : token_is_valid = 0;
63 540 : token_is_filled = 0;
64 6697 : for (i = filter->pattern; *i; ++i) {
65 6158 : 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 6153 : } else if (*i != '.') {
79 5617 : token_is_valid = 1;
80 5617 : token_is_filled = 1;
81 : } else {
82 536 : 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 539 : 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 538 : filter->is_disk = 0;
94 :
95 538 : if (first == 0) {
96 : /* no slash */
97 535 : filter->is_path = 0;
98 535 : 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 537 : 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 527 : void filter_free(struct snapraid_filter* filter)
149 : {
150 527 : free(filter);
151 527 : }
152 :
153 125273 : const char* filter_type(struct snapraid_filter* filter, char* out, size_t out_size)
154 : {
155 : const char* direction;
156 :
157 125273 : if (filter->direction < 0)
158 125006 : direction = "exclude";
159 : else
160 267 : direction = "include";
161 :
162 125273 : if (filter->is_disk)
163 0 : pathprint(out, out_size, "%s %s:", direction, filter->pattern);
164 125273 : else if (filter->is_dir)
165 0 : pathprint(out, out_size, "%s %s/", direction, filter->pattern);
166 : else
167 125273 : pathprint(out, out_size, "%s %s", direction, filter->pattern);
168 :
169 125273 : return out;
170 : }
171 :
172 12457040 : static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* path, const char* name, int is_dir)
173 : {
174 12457040 : int ret = 0;
175 :
176 : /* match dirs with dirs and files with files */
177 12457040 : if (filter->is_dir && !is_dir)
178 2949 : return 0;
179 12454091 : if (!filter->is_dir && is_dir)
180 6223582 : return 0;
181 :
182 6230509 : 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 6227560 : if (fnmatch(filter->pattern, name, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
188 127729 : ret = filter->direction;
189 : }
190 :
191 6230509 : if (reason != 0 && ret < 0)
192 124739 : *reason = filter;
193 :
194 6230509 : return ret;
195 : }
196 :
197 6236627 : 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 6236627 : pathcpy(path, sizeof(path), const_path);
204 :
205 : /* filter for all the directories */
206 6236627 : name = path;
207 89824546 : for (i = 0; path[i] != 0; ++i) {
208 83590909 : if (path[i] == '/') {
209 : /* set a terminator */
210 6223403 : path[i] = 0;
211 :
212 : /* filter the directory */
213 6223403 : if (filter_apply(filter, reason, path, name, 1) != 0)
214 2990 : return filter->direction;
215 :
216 : /* restore the slash */
217 6220413 : path[i] = '/';
218 :
219 : /* next name */
220 6220413 : name = path + i + 1;
221 : }
222 : }
223 :
224 : /* filter the final file */
225 6233637 : if (filter_apply(filter, reason, path, name, is_dir) != 0)
226 124739 : return filter->direction;
227 :
228 6108898 : return 0;
229 : }
230 :
231 3332858 : 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 3332858 : int direction = 1; /* by default include all */
236 :
237 : /* for each filter */
238 9543064 : for (i = tommy_list_head(filterlist); i != 0; i = i->next) {
239 : int ret;
240 6346615 : struct snapraid_filter* filter = i->data;
241 :
242 6346615 : 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 6236627 : ret = filter_recurse(filter, reason, sub, is_dir);
251 : }
252 :
253 6346615 : if (ret > 0) {
254 : /* include the file */
255 11670 : return 0;
256 6334945 : } else if (ret < 0) {
257 : /* exclude the file */
258 124739 : return -1;
259 : } else {
260 : /* default is opposite of the last filter */
261 6210206 : direction = -filter->direction;
262 6210206 : if (reason != 0 && direction < 0)
263 3110900 : *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 3196449 : if (is_def_include)
271 3052 : return 0;
272 :
273 : /* files are excluded/included depending of the last rule processed */
274 3193397 : if (direction < 0)
275 57608 : return -1;
276 :
277 3135789 : return 0;
278 : }
279 :
280 3329761 : int filter_path(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub)
281 : {
282 3329761 : 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 60627 : for (i = 0; i < file->blockmax; ++i) {
329 46377 : block_off_t parity_pos = fs_file2par_get(disk, file, i);
330 46377 : snapraid_info info = info_get(infoarr, parity_pos);
331 :
332 : /* if the file has a bad block, don't exclude it */
333 46377 : if (info_get_bad(info))
334 8298 : return 0;
335 : }
336 :
337 : /* the file is correct, so we filter it out */
338 14250 : return 1;
339 : }
340 :
341 3173346 : int filter_content(tommy_list* contentlist, const char* path)
342 : {
343 : tommy_node* i;
344 :
345 20456460 : for (i = tommy_list_head(contentlist); i != 0; i = i->next) {
346 17283114 : struct snapraid_content* content = i->data;
347 : char tmp[PATH_MAX];
348 :
349 17283114 : if (pathcmp(content->content, path) == 0)
350 0 : return -1;
351 :
352 : /* exclude also the ".tmp" copy used to save it */
353 17283114 : pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
354 17283114 : if (pathcmp(tmp, path) == 0)
355 0 : return -1;
356 :
357 : /* exclude also the ".lock" file */
358 17283114 : pathprint(tmp, sizeof(tmp), "%s.lock", content->content);
359 17283114 : if (pathcmp(tmp, path) == 0)
360 0 : return -1;
361 : }
362 :
363 3173346 : return 0;
364 : }
365 :
366 3695152 : 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 3695152 : file = malloc_nofail(sizeof(struct snapraid_file));
372 3695152 : file->sub = strdup_nofail(sub);
373 3695152 : file->size = size;
374 3695152 : file->blockmax = (size + block_size - 1) / block_size;
375 3695152 : file->mtime_sec = mtime_sec;
376 3695152 : file->mtime_nsec = mtime_nsec;
377 3695152 : file->inode = inode;
378 3695152 : file->physical = physical;
379 3695152 : file->flag = 0;
380 3695152 : file->blockvec = malloc_nofail(file->blockmax * block_sizeof());
381 :
382 12824722 : for (i = 0; i < file->blockmax; ++i) {
383 9129570 : struct snapraid_block* block = file_block(file, i);
384 9129570 : block_state_set(block, BLOCK_STATE_CHG);
385 9129570 : hash_invalid_set(block->hash);
386 : }
387 :
388 3695152 : return file;
389 : }
390 :
391 22686 : struct snapraid_file* file_dup(struct snapraid_file* copy)
392 : {
393 : struct snapraid_file* file;
394 : block_off_t i;
395 :
396 22686 : file = malloc_nofail(sizeof(struct snapraid_file));
397 22686 : file->sub = strdup_nofail(copy->sub);
398 22686 : file->size = copy->size;
399 22686 : file->blockmax = copy->blockmax;
400 22686 : file->mtime_sec = copy->mtime_sec;
401 22686 : file->mtime_nsec = copy->mtime_nsec;
402 22686 : file->inode = copy->inode;
403 22686 : file->physical = copy->physical;
404 22686 : file->flag = copy->flag;
405 22686 : file->blockvec = malloc_nofail(file->blockmax * block_sizeof());
406 :
407 78653 : for (i = 0; i < file->blockmax; ++i) {
408 55967 : struct snapraid_block* block = file_block(file, i);
409 55967 : struct snapraid_block* copy_block = file_block(copy, i);
410 55967 : block->state = copy_block->state;
411 55967 : memcpy(block->hash, copy_block->hash, BLOCK_HASH_SIZE);
412 : }
413 :
414 22686 : return file;
415 : }
416 :
417 3671994 : void file_free(struct snapraid_file* file)
418 : {
419 3671994 : free(file->sub);
420 3671994 : file->sub = 0;
421 3671994 : free(file->blockvec);
422 3671994 : file->blockvec = 0;
423 3671994 : free(file);
424 3671994 : }
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 23637 : void file_copy(struct snapraid_file* src_file, struct snapraid_file* dst_file)
433 : {
434 : block_off_t i;
435 :
436 23637 : if (src_file->size != dst_file->size) {
437 : /* LCOV_EXCL_START */
438 : log_fatal("Internal inconsistency: Copy file with different size\n");
439 : os_abort();
440 : /* LCOV_EXCL_STOP */
441 : }
442 :
443 23637 : if (src_file->mtime_sec != dst_file->mtime_sec) {
444 : /* LCOV_EXCL_START */
445 : log_fatal("Internal inconsistency: Copy file with different mtime_sec\n");
446 : os_abort();
447 : /* LCOV_EXCL_STOP */
448 : }
449 :
450 23637 : if (src_file->mtime_nsec != dst_file->mtime_nsec) {
451 : /* LCOV_EXCL_START */
452 : log_fatal("Internal inconsistency: Copy file with different mtime_nsec\n");
453 : os_abort();
454 : /* LCOV_EXCL_STOP */
455 : }
456 :
457 81881 : for (i = 0; i < dst_file->blockmax; ++i) {
458 : /* set a block with hash computed but without parity */
459 58244 : block_state_set(file_block(dst_file, i), BLOCK_STATE_REP);
460 :
461 : /* copy the hash */
462 58244 : memcpy(file_block(dst_file, i)->hash, file_block(src_file, i)->hash, BLOCK_HASH_SIZE);
463 : }
464 :
465 23637 : file_flag_set(dst_file, FILE_IS_COPY);
466 23637 : }
467 :
468 47668 : const char* file_name(const struct snapraid_file* file)
469 : {
470 47668 : const char* r = strrchr(file->sub, '/');
471 :
472 47668 : if (!r)
473 74 : r = file->sub;
474 : else
475 47594 : ++r;
476 47668 : return r;
477 : }
478 :
479 6821581 : 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 6821581 : if (file_pos + 1 == file->blockmax) {
483 : unsigned block_remainder;
484 2774751 : if (file->size == 0)
485 0 : return 0;
486 2774751 : block_remainder = file->size % block_size;
487 2774751 : if (block_remainder == 0)
488 3060 : block_remainder = block_size;
489 2774751 : return block_remainder;
490 : }
491 :
492 4046830 : return block_size;
493 : }
494 :
495 5252738 : int file_block_is_last(struct snapraid_file* file, block_off_t file_pos)
496 : {
497 5252738 : if (file_pos == 0 && file->blockmax == 0)
498 0 : return 1;
499 :
500 5252738 : if (file_pos >= file->blockmax) {
501 : /* LCOV_EXCL_START */
502 : log_fatal("Internal inconsistency: File block position over the max\n");
503 : os_abort();
504 : /* LCOV_EXCL_STOP */
505 : }
506 :
507 5252738 : return file_pos == file->blockmax - 1;
508 : }
509 :
510 986014 : int file_inode_compare_to_arg(const void* void_arg, const void* void_data)
511 : {
512 986014 : const uint64_t* arg = void_arg;
513 986014 : const struct snapraid_file* file = void_data;
514 :
515 986014 : if (*arg < file->inode)
516 0 : return -1;
517 986014 : if (*arg > file->inode)
518 0 : return 1;
519 986014 : 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 187693 : int file_path_compare(const void* void_a, const void* void_b)
535 : {
536 187693 : const struct snapraid_file* file_a = void_a;
537 187693 : const struct snapraid_file* file_b = void_b;
538 :
539 187693 : return strcmp(file_a->sub, file_b->sub);
540 : }
541 :
542 98524 : int file_physical_compare(const void* void_a, const void* void_b)
543 : {
544 98524 : const struct snapraid_file* file_a = void_a;
545 98524 : const struct snapraid_file* file_b = void_b;
546 :
547 98524 : if (file_a->physical < file_b->physical)
548 51852 : return -1;
549 46672 : if (file_a->physical > file_b->physical)
550 46670 : return 1;
551 2 : return 0;
552 : }
553 :
554 97849 : int file_path_compare_to_arg(const void* void_arg, const void* void_data)
555 : {
556 97849 : const char* arg = void_arg;
557 97849 : const struct snapraid_file* file = void_data;
558 :
559 97849 : return strcmp(arg, file->sub);
560 : }
561 :
562 23834 : int file_name_compare(const void* void_a, const void* void_b)
563 : {
564 23834 : const struct snapraid_file* file_a = void_a;
565 23834 : const struct snapraid_file* file_b = void_b;
566 23834 : const char* name_a = file_name(file_a);
567 23834 : const char* name_b = file_name(file_b);
568 :
569 23834 : return strcmp(name_a, name_b);
570 : }
571 :
572 23669 : int file_stamp_compare(const void* void_a, const void* void_b)
573 : {
574 23669 : const struct snapraid_file* file_a = void_a;
575 23669 : const struct snapraid_file* file_b = void_b;
576 :
577 23669 : if (file_a->size < file_b->size)
578 0 : return -1;
579 23669 : if (file_a->size > file_b->size)
580 0 : return 1;
581 :
582 23669 : if (file_a->mtime_sec < file_b->mtime_sec)
583 0 : return -1;
584 23669 : if (file_a->mtime_sec > file_b->mtime_sec)
585 0 : return 1;
586 :
587 23669 : if (file_a->mtime_nsec < file_b->mtime_nsec)
588 0 : return -1;
589 23669 : if (file_a->mtime_nsec > file_b->mtime_nsec)
590 0 : return 1;
591 :
592 23669 : return 0;
593 : }
594 :
595 23834 : int file_namestamp_compare(const void* void_a, const void* void_b)
596 : {
597 : int ret;
598 :
599 23834 : ret = file_name_compare(void_a, void_b);
600 23834 : if (ret != 0)
601 165 : return ret;
602 :
603 23669 : 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 3739398 : 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 3739398 : if (count == 0) {
622 : /* LCOV_EXCL_START */
623 : log_fatal("Internal inconsistency: 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 3739398 : if (file_pos + count > file->blockmax) {
628 : /* LCOV_EXCL_START */
629 : log_fatal("Internal inconsistency: 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 3739398 : extent = malloc_nofail(sizeof(struct snapraid_extent));
635 3739398 : extent->parity_pos = parity_pos;
636 3739398 : extent->file = file;
637 3739398 : extent->file_pos = file_pos;
638 3739398 : extent->count = count;
639 :
640 3739398 : return extent;
641 : }
642 :
643 3693615 : void extent_free(struct snapraid_extent* extent)
644 : {
645 3693615 : free(extent);
646 3693615 : }
647 :
648 44529607 : int extent_parity_compare(const void* void_a, const void* void_b)
649 : {
650 44529607 : const struct snapraid_extent* arg_a = void_a;
651 44529607 : const struct snapraid_extent* arg_b = void_b;
652 :
653 44529607 : if (arg_a->parity_pos < arg_b->parity_pos)
654 937934 : return -1;
655 43591673 : if (arg_a->parity_pos > arg_b->parity_pos)
656 43553809 : return 1;
657 :
658 37864 : return 0;
659 : }
660 :
661 44887416 : int extent_file_compare(const void* void_a, const void* void_b)
662 : {
663 44887416 : const struct snapraid_extent* arg_a = void_a;
664 44887416 : const struct snapraid_extent* arg_b = void_b;
665 :
666 44887416 : if (arg_a->file < arg_b->file)
667 1029349 : return -1;
668 43858067 : if (arg_a->file > arg_b->file)
669 43789130 : return 1;
670 :
671 68937 : if (arg_a->file_pos < arg_b->file_pos)
672 186 : return -1;
673 68751 : if (arg_a->file_pos > arg_b->file_pos)
674 30887 : return 1;
675 :
676 37864 : return 0;
677 : }
678 :
679 119733 : struct snapraid_link* link_alloc(const char* sub, const char* linkto, unsigned link_flag)
680 : {
681 : struct snapraid_link* slink;
682 :
683 119733 : slink = malloc_nofail(sizeof(struct snapraid_link));
684 119733 : slink->sub = strdup_nofail(sub);
685 119733 : slink->linkto = strdup_nofail(linkto);
686 119733 : slink->flag = link_flag;
687 :
688 119733 : return slink;
689 : }
690 :
691 118137 : void link_free(struct snapraid_link* slink)
692 : {
693 118137 : free(slink->sub);
694 118137 : free(slink->linkto);
695 118137 : free(slink);
696 118137 : }
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 792 : void dir_free(struct snapraid_dir* dir)
726 : {
727 792 : free(dir->sub);
728 792 : free(dir);
729 792 : }
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 1603 : struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev, const char* uuid, int skip_access)
740 : {
741 : struct snapraid_disk* disk;
742 : unsigned i;
743 :
744 1603 : disk = malloc_nofail(sizeof(struct snapraid_disk));
745 1603 : pathcpy(disk->name, sizeof(disk->name), name);
746 1603 : pathimport(disk->dir, sizeof(disk->dir), dir);
747 1603 : pathcpy(disk->uuid, sizeof(disk->uuid), uuid);
748 :
749 : /* ensure that the dir terminate with "/" if it isn't empty */
750 1603 : pathslash(disk->dir, sizeof(disk->dir));
751 :
752 : #if HAVE_THREAD
753 1603 : thread_mutex_init(&disk->fs_mutex);
754 1603 : disk->fs_mutex_enabled = 0; /* lock will be enabled at threads start */
755 : #endif
756 :
757 1603 : disk->smartctl[0] = 0;
758 8015 : for (i = 0; i < SMART_IGNORE_MAX; ++i)
759 6412 : disk->smartignore[i] = 0;
760 1603 : disk->device = dev;
761 1603 : disk->tick = 0;
762 1603 : disk->cached_blocks = 0;
763 1603 : disk->progress_file = 0;
764 1603 : disk->total_blocks = 0;
765 1603 : disk->free_blocks = 0;
766 1603 : disk->first_free_block = 0;
767 1603 : disk->has_volatile_inodes = 0;
768 1603 : disk->has_volatile_hardlinks = 0;
769 1603 : disk->has_unreliable_physical = 0;
770 1603 : disk->has_different_uuid = 0;
771 1603 : disk->has_unsupported_uuid = *uuid == 0; /* empty UUID means unsupported */
772 1603 : disk->had_empty_uuid = 0;
773 1603 : disk->mapping_idx = -1;
774 1603 : disk->skip_access = skip_access;
775 1603 : tommy_list_init(&disk->filelist);
776 1603 : tommy_list_init(&disk->deletedlist);
777 1603 : tommy_hashdyn_init(&disk->inodeset);
778 1603 : tommy_hashdyn_init(&disk->pathset);
779 1603 : tommy_hashdyn_init(&disk->stampset);
780 1603 : tommy_list_init(&disk->linklist);
781 1603 : tommy_hashdyn_init(&disk->linkset);
782 1603 : tommy_list_init(&disk->dirlist);
783 1603 : tommy_hashdyn_init(&disk->dirset);
784 1603 : tommy_tree_init(&disk->fs_parity, extent_parity_compare);
785 1603 : tommy_tree_init(&disk->fs_file, extent_file_compare);
786 1603 : disk->fs_last = 0;
787 :
788 1603 : return disk;
789 : }
790 :
791 1543 : void disk_free(struct snapraid_disk* disk)
792 : {
793 1543 : tommy_list_foreach(&disk->filelist, (tommy_foreach_func*)file_free);
794 1543 : tommy_list_foreach(&disk->deletedlist, (tommy_foreach_func*)file_free);
795 1543 : tommy_tree_foreach(&disk->fs_file, (tommy_foreach_func*)extent_free);
796 1543 : tommy_hashdyn_done(&disk->inodeset);
797 1543 : tommy_hashdyn_done(&disk->pathset);
798 1543 : tommy_hashdyn_done(&disk->stampset);
799 1543 : tommy_list_foreach(&disk->linklist, (tommy_foreach_func*)link_free);
800 1543 : tommy_hashdyn_done(&disk->linkset);
801 1543 : tommy_list_foreach(&disk->dirlist, (tommy_foreach_func*)dir_free);
802 1543 : tommy_hashdyn_done(&disk->dirset);
803 :
804 : #if HAVE_THREAD
805 1543 : thread_mutex_destroy(&disk->fs_mutex);
806 : #endif
807 :
808 1543 : free(disk);
809 1543 : }
810 :
811 574 : void disk_start_thread(struct snapraid_disk* disk)
812 : {
813 : #if HAVE_THREAD
814 574 : disk->fs_mutex_enabled = 1;
815 : #else
816 : (void)disk;
817 : #endif
818 574 : }
819 :
820 50014462 : static inline void fs_lock(struct snapraid_disk* disk)
821 : {
822 : #if HAVE_THREAD
823 50014462 : if (disk->fs_mutex_enabled)
824 6272719 : thread_mutex_lock(&disk->fs_mutex);
825 : #else
826 : (void)disk;
827 : #endif
828 50014462 : }
829 :
830 50014462 : static inline void fs_unlock(struct snapraid_disk* disk)
831 : {
832 : #if HAVE_THREAD
833 50014462 : if (disk->fs_mutex_enabled)
834 6272719 : thread_mutex_unlock(&disk->fs_mutex);
835 : #else
836 : (void)disk;
837 : #endif
838 50014462 : }
839 :
840 : struct extent_disk_empty {
841 : block_off_t blockmax;
842 : };
843 :
844 : /**
845 : * Compare the extent if inside the specified blockmax.
846 : */
847 12 : static int extent_disk_empty_compare_unlock(const void* void_a, const void* void_b)
848 : {
849 12 : const struct extent_disk_empty* arg_a = void_a;
850 12 : const struct snapraid_extent* arg_b = void_b;
851 :
852 : /* if the block is inside the specified blockmax, it's found */
853 12 : if (arg_a->blockmax > arg_b->parity_pos)
854 7 : return 0;
855 :
856 : /* otherwise search for a smaller one */
857 5 : return -1;
858 : }
859 :
860 876 : int fs_is_empty(struct snapraid_disk* disk, block_off_t blockmax)
861 : {
862 876 : struct extent_disk_empty arg = { blockmax };
863 :
864 : /* if there is an element, it's not empty */
865 : /* even if links and dirs have no block allocation */
866 876 : if (!tommy_list_empty(&disk->filelist))
867 777 : return 0;
868 99 : if (!tommy_list_empty(&disk->linklist))
869 0 : return 0;
870 99 : if (!tommy_list_empty(&disk->dirlist))
871 0 : return 0;
872 :
873 99 : fs_lock(disk);
874 :
875 : /* search for any extent inside blockmax */
876 99 : if (tommy_tree_search_compare(&disk->fs_parity, extent_disk_empty_compare_unlock, &arg) != 0) {
877 7 : fs_unlock(disk);
878 7 : return 0;
879 : }
880 :
881 : /* finally, it's empty */
882 92 : fs_unlock(disk);
883 92 : return 1;
884 : }
885 :
886 : struct extent_disk_size {
887 : block_off_t size;
888 : };
889 :
890 : /**
891 : * Compare the extent by highest parity position.
892 : *
893 : * The maximum parity position is stored as size.
894 : */
895 50271 : static int extent_disk_size_compare_unlock(const void* void_a, const void* void_b)
896 : {
897 50271 : struct extent_disk_size* arg_a = (void*)void_a;
898 50271 : const struct snapraid_extent* arg_b = void_b;
899 :
900 : /* get the maximum size */
901 50271 : if (arg_a->size < arg_b->parity_pos + arg_b->count)
902 50271 : arg_a->size = arg_b->parity_pos + arg_b->count;
903 :
904 : /* search always for a bigger one */
905 50271 : return 1;
906 : }
907 :
908 4345 : block_off_t fs_size(struct snapraid_disk* disk)
909 : {
910 4345 : struct extent_disk_size arg = { 0 };
911 :
912 4345 : fs_lock(disk);
913 :
914 4345 : tommy_tree_search_compare(&disk->fs_parity, extent_disk_size_compare_unlock, &arg);
915 :
916 4345 : fs_unlock(disk);
917 :
918 4345 : return arg.size;
919 : }
920 :
921 : struct extent_check {
922 : const struct snapraid_extent* prev;
923 : int result;
924 : };
925 :
926 6539077 : static void extent_parity_check_foreach_unlock(void* void_arg, void* void_obj)
927 : {
928 6539077 : struct extent_check* arg = void_arg;
929 6539077 : const struct snapraid_extent* obj = void_obj;
930 6539077 : const struct snapraid_extent* prev = arg->prev;
931 :
932 : /* set the next previous block */
933 6539077 : arg->prev = obj;
934 :
935 : /* stop reporting if too many errors */
936 6539077 : if (arg->result > 100) {
937 : /* LCOV_EXCL_START */
938 : return;
939 : /* LCOV_EXCL_STOP */
940 : }
941 :
942 6539077 : if (obj->count == 0) {
943 : /* LCOV_EXCL_START */
944 : log_fatal("Internal inconsistency: Parity count zero for file '%s' at '%u'\n",
945 : obj->file->sub, obj->parity_pos);
946 : ++arg->result;
947 : return;
948 : /* LCOV_EXCL_STOP */
949 : }
950 :
951 : /* check only if there is a previous block */
952 6539077 : if (!prev)
953 2699 : return;
954 :
955 : /* check the order */
956 6536378 : if (prev->parity_pos >= obj->parity_pos) {
957 : /* LCOV_EXCL_START */
958 : log_fatal("Internal inconsistency: Parity order for files '%s' at '%u:%u' and '%s' at '%u:%u'\n",
959 : prev->file->sub, prev->parity_pos, prev->count, obj->file->sub, obj->parity_pos, obj->count);
960 : ++arg->result;
961 : return;
962 : /* LCOV_EXCL_STOP */
963 : }
964 :
965 : /* check that the extents don't overlap */
966 6536378 : if (prev->parity_pos + prev->count > obj->parity_pos) {
967 : /* LCOV_EXCL_START */
968 : log_fatal("Internal inconsistency: Parity overlap for files '%s' at '%u:%u' and '%s' at '%u:%u'\n",
969 : prev->file->sub, prev->parity_pos, prev->count, obj->file->sub, obj->parity_pos, obj->count);
970 : ++arg->result;
971 : return;
972 : /* LCOV_EXCL_STOP */
973 : }
974 : }
975 :
976 6539077 : static void extent_file_check_foreach_unlock(void* void_arg, void* void_obj)
977 : {
978 6539077 : struct extent_check* arg = void_arg;
979 6539077 : const struct snapraid_extent* obj = void_obj;
980 6539077 : const struct snapraid_extent* prev = arg->prev;
981 :
982 : /* set the next previous block */
983 6539077 : arg->prev = obj;
984 :
985 : /* stop reporting if too many errors */
986 6539077 : if (arg->result > 100) {
987 : /* LCOV_EXCL_START */
988 : return;
989 : /* LCOV_EXCL_STOP */
990 : }
991 :
992 6539077 : if (obj->count == 0) {
993 : /* LCOV_EXCL_START */
994 : log_fatal("Internal inconsistency: File count zero for file '%s' at '%u'\n",
995 : obj->file->sub, obj->file_pos);
996 : ++arg->result;
997 : return;
998 : /* LCOV_EXCL_STOP */
999 : }
1000 :
1001 : /* note that for deleted files, some extents may be missing */
1002 :
1003 : /* if the files are different */
1004 6539077 : if (!prev || prev->file != obj->file) {
1005 6493242 : if (prev != 0) {
1006 6490543 : if (file_flag_has(prev->file, FILE_IS_DELETED)) {
1007 : /* check that the extent doesn't overflow the file */
1008 59770 : if (prev->file_pos + prev->count > prev->file->blockmax) {
1009 : /* LCOV_EXCL_START */
1010 : log_fatal("Internal inconsistency: Delete end for file '%s' at '%u:%u' overflowing size '%u'\n",
1011 : prev->file->sub, prev->file_pos, prev->count, prev->file->blockmax);
1012 : ++arg->result;
1013 : return;
1014 : /* LCOV_EXCL_STOP */
1015 : }
1016 : } else {
1017 : /* check that the extent ends the file */
1018 6430773 : if (prev->file_pos + prev->count != prev->file->blockmax) {
1019 : /* LCOV_EXCL_START */
1020 : log_fatal("Internal inconsistency: File end for file '%s' at '%u:%u' instead of size '%u'\n",
1021 : prev->file->sub, prev->file_pos, prev->count, prev->file->blockmax);
1022 : ++arg->result;
1023 : return;
1024 : /* LCOV_EXCL_STOP */
1025 : }
1026 : }
1027 : }
1028 :
1029 6493242 : if (file_flag_has(obj->file, FILE_IS_DELETED)) {
1030 : /* check that the extent doesn't overflow the file */
1031 59844 : if (obj->file_pos + obj->count > obj->file->blockmax) {
1032 : /* LCOV_EXCL_START */
1033 : log_fatal("Internal inconsistency: Delete start for file '%s' at '%u:%u' overflowing size '%u'\n",
1034 : obj->file->sub, obj->file_pos, obj->count, obj->file->blockmax);
1035 : ++arg->result;
1036 : return;
1037 : /* LCOV_EXCL_STOP */
1038 : }
1039 : } else {
1040 : /* check that the extent starts the file */
1041 6433398 : if (obj->file_pos != 0) {
1042 : /* LCOV_EXCL_START */
1043 : log_fatal("Internal inconsistency: File start for file '%s' at '%u:%u'\n",
1044 : obj->file->sub, obj->file_pos, obj->count);
1045 : ++arg->result;
1046 : return;
1047 : /* LCOV_EXCL_STOP */
1048 : }
1049 : }
1050 : } else {
1051 : /* check the order */
1052 45835 : if (prev->file_pos >= obj->file_pos) {
1053 : /* LCOV_EXCL_START */
1054 : log_fatal("Internal inconsistency: File order for file '%s' at '%u:%u' and at '%u:%u'\n",
1055 : prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
1056 : ++arg->result;
1057 : return;
1058 : /* LCOV_EXCL_STOP */
1059 : }
1060 :
1061 45835 : if (file_flag_has(obj->file, FILE_IS_DELETED)) {
1062 : /* check that the extents don't overlap */
1063 27 : if (prev->file_pos + prev->count > obj->file_pos) {
1064 : /* LCOV_EXCL_START */
1065 : log_fatal("Internal inconsistency: Delete sequence for file '%s' at '%u:%u' and at '%u:%u'\n",
1066 : prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
1067 : ++arg->result;
1068 : return;
1069 : /* LCOV_EXCL_STOP */
1070 : }
1071 : } else {
1072 : /* check that the extents are sequential */
1073 45808 : if (prev->file_pos + prev->count != obj->file_pos) {
1074 : /* LCOV_EXCL_START */
1075 : log_fatal("Internal inconsistency: File sequence for file '%s' at '%u:%u' and at '%u:%u'\n",
1076 : prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
1077 : ++arg->result;
1078 : return;
1079 : /* LCOV_EXCL_STOP */
1080 : }
1081 : }
1082 : }
1083 : }
1084 :
1085 2964 : int fs_check(struct snapraid_disk* disk)
1086 : {
1087 : struct extent_check arg;
1088 :
1089 : /* error count starts from 0 */
1090 2964 : arg.result = 0;
1091 :
1092 2964 : fs_lock(disk);
1093 :
1094 : /* check parity sequence */
1095 2964 : arg.prev = 0;
1096 2964 : tommy_tree_foreach_arg(&disk->fs_parity, extent_parity_check_foreach_unlock, &arg);
1097 :
1098 : /* check file sequence */
1099 2964 : arg.prev = 0;
1100 2964 : tommy_tree_foreach_arg(&disk->fs_file, extent_file_check_foreach_unlock, &arg);
1101 :
1102 2964 : fs_unlock(disk);
1103 :
1104 2964 : if (arg.result != 0)
1105 0 : return -1;
1106 :
1107 2964 : return 0;
1108 : }
1109 :
1110 : struct extent_parity_inside {
1111 : block_off_t parity_pos;
1112 : };
1113 :
1114 : /**
1115 : * Compare the extent if containing the specified parity position.
1116 : */
1117 102369080 : static int extent_parity_inside_compare_unlock(const void* void_a, const void* void_b)
1118 : {
1119 102369080 : const struct extent_parity_inside* arg_a = void_a;
1120 102369080 : const struct snapraid_extent* arg_b = void_b;
1121 :
1122 102369080 : if (arg_a->parity_pos < arg_b->parity_pos)
1123 35826212 : return -1;
1124 66542868 : if (arg_a->parity_pos >= arg_b->parity_pos + arg_b->count)
1125 58783196 : return 1;
1126 :
1127 7759672 : return 0;
1128 : }
1129 :
1130 : /**
1131 : * Search the extent at the specified parity position.
1132 : * The search is optimized for sequential accesses.
1133 : * \return If not found return 0
1134 : */
1135 36077728 : static struct snapraid_extent* fs_par2extent_get_unlock(struct snapraid_disk* disk, struct snapraid_extent** fs_last, block_off_t parity_pos)
1136 : {
1137 : struct snapraid_extent* extent;
1138 :
1139 : /* check if the last accessed extent matches */
1140 36077728 : if (*fs_last
1141 36060384 : && parity_pos >= (*fs_last)->parity_pos
1142 35395753 : && parity_pos < (*fs_last)->parity_pos + (*fs_last)->count
1143 : ) {
1144 26841485 : extent = *fs_last;
1145 : } else {
1146 9236243 : struct extent_parity_inside arg = { parity_pos };
1147 9236243 : extent = tommy_tree_search_compare(&disk->fs_parity, extent_parity_inside_compare_unlock, &arg);
1148 : }
1149 :
1150 36077728 : if (!extent)
1151 1476571 : return 0;
1152 :
1153 : /* store the last accessed extent */
1154 34601157 : *fs_last = extent;
1155 :
1156 34601157 : return extent;
1157 : }
1158 :
1159 : struct extent_file_inside {
1160 : struct snapraid_file* file;
1161 : block_off_t file_pos;
1162 : };
1163 :
1164 : /**
1165 : * Compare the extent if containing the specified file position.
1166 : */
1167 20747525 : static int extent_file_inside_compare_unlock(const void* void_a, const void* void_b)
1168 : {
1169 20747525 : const struct extent_file_inside* arg_a = void_a;
1170 20747525 : const struct snapraid_extent* arg_b = void_b;
1171 :
1172 20747525 : if (arg_a->file < arg_b->file)
1173 8708594 : return -1;
1174 12038931 : if (arg_a->file > arg_b->file)
1175 10073461 : return 1;
1176 :
1177 1965470 : if (arg_a->file_pos < arg_b->file_pos)
1178 7428 : return -1;
1179 1958042 : if (arg_a->file_pos >= arg_b->file_pos + arg_b->count)
1180 8336 : return 1;
1181 :
1182 1949706 : return 0;
1183 : }
1184 :
1185 : /**
1186 : * Search the extent at the specified file position.
1187 : * The search is optimized for sequential accesses.
1188 : * \return If not found return 0
1189 : */
1190 10216552 : 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)
1191 : {
1192 : struct snapraid_extent* extent;
1193 :
1194 : /* check if the last accessed extent matches */
1195 10216552 : if (*fs_last
1196 10194046 : && file == (*fs_last)->file
1197 8280735 : && file_pos >= (*fs_last)->file_pos
1198 8280735 : && file_pos < (*fs_last)->file_pos + (*fs_last)->count
1199 : ) {
1200 8266838 : extent = *fs_last;
1201 : } else {
1202 1949714 : struct extent_file_inside arg = { file, file_pos };
1203 1949714 : extent = tommy_tree_search_compare(&disk->fs_file, extent_file_inside_compare_unlock, &arg);
1204 : }
1205 :
1206 10216552 : if (!extent)
1207 8 : return 0;
1208 :
1209 : /* store the last accessed extent */
1210 10216544 : *fs_last = extent;
1211 :
1212 10216544 : return extent;
1213 : }
1214 :
1215 35975899 : struct snapraid_file* fs_par2file_find(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
1216 : {
1217 : struct snapraid_extent* extent;
1218 : struct snapraid_file* file;
1219 :
1220 35975899 : fs_lock(disk);
1221 :
1222 35975899 : extent = fs_par2extent_get_unlock(disk, &disk->fs_last, parity_pos);
1223 :
1224 35975899 : if (!extent) {
1225 1476571 : fs_unlock(disk);
1226 1476571 : return 0;
1227 : }
1228 :
1229 34499328 : if (file_pos)
1230 34294750 : *file_pos = extent->file_pos + (parity_pos - extent->parity_pos);
1231 :
1232 34499328 : file = extent->file;
1233 :
1234 34499328 : fs_unlock(disk);
1235 34499328 : return file;
1236 : }
1237 :
1238 4743789 : block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
1239 : {
1240 : struct snapraid_extent* extent;
1241 : block_off_t ret;
1242 :
1243 4743789 : fs_lock(disk);
1244 :
1245 4743789 : extent = fs_file2extent_get_unlock(disk, &disk->fs_last, file, file_pos);
1246 4743789 : if (!extent) {
1247 8 : fs_unlock(disk);
1248 8 : return POS_NULL;
1249 : }
1250 :
1251 4743781 : ret = extent->parity_pos + (file_pos - extent->file_pos);
1252 :
1253 4743781 : fs_unlock(disk);
1254 4743781 : return ret;
1255 : }
1256 :
1257 9185537 : void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos)
1258 : {
1259 : struct snapraid_extent* extent;
1260 : struct snapraid_extent* parity_extent;
1261 : struct snapraid_extent* file_extent;
1262 :
1263 9185537 : fs_lock(disk);
1264 :
1265 9185537 : if (file_pos > 0) {
1266 : /* search an existing extent for the previous file_pos */
1267 5472763 : extent = fs_file2extent_get_unlock(disk, &disk->fs_last, file, file_pos - 1);
1268 :
1269 5472763 : if (extent != 0 && parity_pos == extent->parity_pos + extent->count) {
1270 : /* ensure that we are extending the extent at the end */
1271 5446140 : if (file_pos != extent->file_pos + extent->count) {
1272 : /* LCOV_EXCL_START */
1273 : log_fatal("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);
1274 : os_abort();
1275 : /* LCOV_EXCL_STOP */
1276 : }
1277 :
1278 : /* extend the existing extent */
1279 5446140 : ++extent->count;
1280 :
1281 5446140 : fs_unlock(disk);
1282 5446140 : return;
1283 : }
1284 : }
1285 :
1286 : /* a extent doesn't exist, and we have to create a new one */
1287 3739397 : extent = extent_alloc(parity_pos, file, file_pos, 1);
1288 :
1289 : /* insert the extent in the trees */
1290 3739397 : parity_extent = tommy_tree_insert(&disk->fs_parity, &extent->parity_node, extent);
1291 3739397 : file_extent = tommy_tree_insert(&disk->fs_file, &extent->file_node, extent);
1292 :
1293 3739397 : if (parity_extent != extent || file_extent != extent) {
1294 : /* LCOV_EXCL_START */
1295 : log_fatal("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);
1296 : os_abort();
1297 : /* LCOV_EXCL_STOP */
1298 : }
1299 :
1300 : /* store the last accessed extent */
1301 3739397 : disk->fs_last = extent;
1302 :
1303 3739397 : fs_unlock(disk);
1304 : }
1305 :
1306 101829 : void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos)
1307 : {
1308 : struct snapraid_extent* extent;
1309 : struct snapraid_extent* second_extent;
1310 : struct snapraid_extent* parity_extent;
1311 : struct snapraid_extent* file_extent;
1312 : block_off_t first_count, second_count;
1313 :
1314 101829 : fs_lock(disk);
1315 :
1316 101829 : extent = fs_par2extent_get_unlock(disk, &disk->fs_last, parity_pos);
1317 101829 : if (!extent) {
1318 : /* LCOV_EXCL_START */
1319 : log_fatal("Internal inconsistency: Deallocating parity position '%u' for not existing extent in disk '%s'\n", parity_pos, disk->name);
1320 : os_abort();
1321 : /* LCOV_EXCL_STOP */
1322 : }
1323 :
1324 : /* if it's the only block of the extent, delete it */
1325 101829 : if (extent->count == 1) {
1326 : /* remove from the trees */
1327 37864 : tommy_tree_remove(&disk->fs_parity, extent);
1328 37864 : tommy_tree_remove(&disk->fs_file, extent);
1329 :
1330 : /* deallocate */
1331 37864 : extent_free(extent);
1332 :
1333 : /* clear the last accessed extent */
1334 37864 : disk->fs_last = 0;
1335 :
1336 37864 : fs_unlock(disk);
1337 37864 : return;
1338 : }
1339 :
1340 : /* if it's at the start of the extent, shrink the extent */
1341 63965 : if (parity_pos == extent->parity_pos) {
1342 63963 : ++extent->parity_pos;
1343 63963 : ++extent->file_pos;
1344 63963 : --extent->count;
1345 :
1346 63963 : fs_unlock(disk);
1347 63963 : return;
1348 : }
1349 :
1350 : /* if it's at the end of the extent, shrink the extent */
1351 2 : if (parity_pos == extent->parity_pos + extent->count - 1) {
1352 1 : --extent->count;
1353 :
1354 1 : fs_unlock(disk);
1355 1 : return;
1356 : }
1357 :
1358 : /* otherwise it's in the middle */
1359 1 : first_count = parity_pos - extent->parity_pos;
1360 1 : second_count = extent->count - first_count - 1;
1361 :
1362 : /* adjust the first extent */
1363 1 : extent->count = first_count;
1364 :
1365 : /* allocate the second extent */
1366 1 : second_extent = extent_alloc(extent->parity_pos + first_count + 1, extent->file, extent->file_pos + first_count + 1, second_count);
1367 :
1368 : /* insert the extent in the trees */
1369 1 : parity_extent = tommy_tree_insert(&disk->fs_parity, &second_extent->parity_node, second_extent);
1370 1 : file_extent = tommy_tree_insert(&disk->fs_file, &second_extent->file_node, second_extent);
1371 :
1372 1 : if (parity_extent != second_extent || file_extent != second_extent) {
1373 : /* LCOV_EXCL_START */
1374 : log_fatal("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);
1375 : os_abort();
1376 : /* LCOV_EXCL_STOP */
1377 : }
1378 :
1379 : /* store the last accessed extent */
1380 1 : disk->fs_last = second_extent;
1381 :
1382 1 : fs_unlock(disk);
1383 : }
1384 :
1385 41961621 : struct snapraid_block* fs_file2block_get(struct snapraid_file* file, block_off_t file_pos)
1386 : {
1387 41961621 : if (file_pos >= file->blockmax) {
1388 : /* LCOV_EXCL_START */
1389 : log_fatal("Internal inconsistency: Dereferencing file '%s' at position '%u/%u'\n", file->sub, file_pos, file->blockmax);
1390 : os_abort();
1391 : /* LCOV_EXCL_STOP */
1392 : }
1393 :
1394 41961621 : return file_block(file, file_pos);
1395 : }
1396 :
1397 24465312 : struct snapraid_block* fs_par2block_find(struct snapraid_disk* disk, block_off_t parity_pos)
1398 : {
1399 : struct snapraid_file* file;
1400 : block_off_t file_pos;
1401 :
1402 24465312 : file = fs_par2file_find(disk, parity_pos, &file_pos);
1403 24465312 : if (file == 0)
1404 1476571 : return BLOCK_NULL;
1405 :
1406 22988741 : return fs_file2block_get(file, file_pos);
1407 : }
1408 :
1409 1567 : struct snapraid_map* map_alloc(const char* name, unsigned position, block_off_t total_blocks, block_off_t free_blocks, const char* uuid)
1410 : {
1411 : struct snapraid_map* map;
1412 :
1413 1567 : map = malloc_nofail(sizeof(struct snapraid_map));
1414 1567 : pathcpy(map->name, sizeof(map->name), name);
1415 1567 : map->position = position;
1416 1567 : map->total_blocks = total_blocks;
1417 1567 : map->free_blocks = free_blocks;
1418 1567 : pathcpy(map->uuid, sizeof(map->uuid), uuid);
1419 :
1420 1567 : return map;
1421 : }
1422 :
1423 1513 : void map_free(struct snapraid_map* map)
1424 : {
1425 1513 : free(map);
1426 1513 : }
1427 :
1428 678904 : int time_compare(const void* void_a, const void* void_b)
1429 : {
1430 678904 : const time_t* time_a = void_a;
1431 678904 : const time_t* time_b = void_b;
1432 :
1433 678904 : if (*time_a < *time_b)
1434 10501 : return -1;
1435 668403 : if (*time_a > *time_b)
1436 114443 : return 1;
1437 553960 : return 0;
1438 : }
1439 :
1440 : /****************************************************************************/
1441 : /* format */
1442 :
1443 : int FMT_MODE = FMT_FILE;
1444 :
1445 : /**
1446 : * Format a file path for poll reference
1447 : */
1448 6 : const char* fmt_poll(const struct snapraid_disk* disk, const char* str, char* buffer)
1449 : {
1450 : (void)disk;
1451 6 : return esc_shell(str, buffer);
1452 : }
1453 :
1454 : /**
1455 : * Format a path name for terminal reference
1456 : */
1457 357842 : const char* fmt_term(const struct snapraid_disk* disk, const char* str, char* buffer)
1458 : {
1459 : const char* out[3];
1460 :
1461 357842 : switch (FMT_MODE) {
1462 333782 : case FMT_FILE :
1463 : default :
1464 333782 : return esc_shell(str, buffer);
1465 12030 : case FMT_DISK :
1466 12030 : out[0] = disk->name;
1467 12030 : out[1] = ":";
1468 12030 : out[2] = str;
1469 12030 : return esc_shell_multi(out, 3, buffer);
1470 12030 : case FMT_PATH :
1471 12030 : out[0] = disk->dir;
1472 12030 : out[1] = str;
1473 12030 : return esc_shell_multi(out, 2, buffer);
1474 : }
1475 : }
1476 :
|