LCOV - code coverage report
Current view: top level - cmdline - elem.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 702 738 95.1 %
Date: 2026-04-29 15:04:44 Functions: 75 77 97.4 %

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

Generated by: LCOV version 1.0