LCOV - code coverage report
Current view: top level - cmdline - elem.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 610 645 94.6 %
Date: 2025-10-28 11:59:11 Functions: 68 70 97.1 %

          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             : 

Generated by: LCOV version 1.0