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

Generated by: LCOV version 1.13