LCOV - code coverage report
Current view: top level - cmdline - parity.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 241 263 91.6 %
Date: 2025-10-28 11:59:11 Functions: 19 20 95.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011 Andrea Mazzoleni
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "portable.h"
      19             : 
      20             : #include "support.h"
      21             : #include "elem.h"
      22             : #include "state.h"
      23             : #include "parity.h"
      24             : #include "handle.h"
      25             : 
      26             : /**
      27             :  * Pseudo random limits for parity
      28             :  */
      29             : #define PARITY_LIMIT(size, split, level) \
      30             :         size ? size + (123562341 + split * 634542351 + level * 983491341) % size : 0
      31             : 
      32             : /****************************************************************************/
      33             : /* parity */
      34             : 
      35         639 : block_off_t parity_allocated_size(struct snapraid_state* state)
      36             : {
      37             :         block_off_t parity_block;
      38             :         tommy_node* i;
      39             : 
      40             :         /* compute the size of the parity file */
      41         639 :         parity_block = 0;
      42        4463 :         for (i = state->disklist; i != 0; i = i->next) {
      43        3824 :                 struct snapraid_disk* disk = i->data;
      44             : 
      45             :                 /* start from the declared size */
      46        3824 :                 block_off_t block = fs_size(disk);
      47             : 
      48             :                 /* decrease the block until an allocated one, but part of a file */
      49             :                 /* we don't stop at deleted blocks, because we want to have them cleared */
      50             :                 /* if they are at the end of the parity */
      51      186374 :                 while (block > parity_block && !block_has_file(fs_par2block_find(disk, block - 1)))
      52      182550 :                         --block;
      53             : 
      54             :                 /* get the highest value */
      55        3824 :                 if (block > parity_block)
      56        2252 :                         parity_block = block;
      57             :         }
      58             : 
      59         639 :         return parity_block;
      60             : }
      61             : 
      62          87 : block_off_t parity_used_size(struct snapraid_state* state)
      63             : {
      64             :         block_off_t parity_block;
      65             :         tommy_node* i;
      66             : 
      67             :         /* compute the size of the parity file */
      68          87 :         parity_block = 0;
      69         608 :         for (i = state->disklist; i != 0; i = i->next) {
      70         521 :                 struct snapraid_disk* disk = i->data;
      71             : 
      72             :                 /* start from the declared size */
      73         521 :                 block_off_t block = fs_size(disk);
      74             : 
      75             :                 /* decrease the block until an used one */
      76      221195 :                 while (block > parity_block && !block_has_file_and_valid_parity(fs_par2block_find(disk, block - 1)))
      77      220674 :                         --block;
      78             : 
      79             :                 /* get the highest value */
      80         521 :                 if (block > parity_block)
      81         286 :                         parity_block = block;
      82             :         }
      83             : 
      84          87 :         return parity_block;
      85             : }
      86             : 
      87           2 : int parity_is_invalid(struct snapraid_state* state)
      88             : {
      89             :         block_off_t blockmax;
      90             :         block_off_t i;
      91             : 
      92           2 :         blockmax = parity_allocated_size(state);
      93             : 
      94           2 :         for (i = 0; i < blockmax; ++i) {
      95             :                 tommy_node* node_disk;
      96             :                 int one_invalid;
      97             :                 int one_valid;
      98             : 
      99             :                 /* for each disk */
     100           1 :                 one_invalid = 0;
     101           1 :                 one_valid = 0;
     102           7 :                 for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) {
     103           6 :                         struct snapraid_disk* disk = node_disk->data;
     104           6 :                         struct snapraid_block* block = fs_par2block_find(disk, i);
     105             : 
     106           6 :                         if (block_has_file(block))
     107           5 :                                 one_valid = 1;
     108           6 :                         if (block_has_invalid_parity(block))
     109           2 :                                 one_invalid = 1;
     110             :                 }
     111             : 
     112             :                 /* if both valid and invalid, we need to update */
     113           1 :                 if (one_invalid && one_valid)
     114           1 :                         return 1;
     115             :         }
     116             : 
     117           1 :         return 0;
     118             : }
     119             : 
     120           0 : void parity_overflow(struct snapraid_state* state, data_off_t size)
     121             : {
     122             :         tommy_node* i;
     123             :         block_off_t blockalloc;
     124           0 :         int found = 0;
     125             :         char esc_buffer[ESC_MAX];
     126             : 
     127             :         /* don't report if everything is outside or if the file is not accessible */
     128           0 :         if (size == 0) {
     129           0 :                 return;
     130             :         }
     131             : 
     132           0 :         blockalloc = size / state->block_size;
     133             : 
     134             :         /* for all disks */
     135           0 :         for (i = state->disklist; i != 0; i = i->next) {
     136           0 :                 struct snapraid_disk* disk = i->data;
     137             :                 tommy_node* j;
     138             : 
     139             :                 /* for all files */
     140           0 :                 for (j = disk->filelist; j != 0; j = j->next) {
     141           0 :                         struct snapraid_file* file = j->data;
     142             : 
     143           0 :                         if (file->blockmax > 0) {
     144           0 :                                 block_off_t parity_pos = fs_file2par_get(disk, file, file->blockmax - 1);
     145           0 :                                 if (parity_pos >= blockalloc) {
     146           0 :                                         found = 1;
     147           0 :                                         log_tag("outofparity:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
     148           0 :                                         log_fatal("outofparity %s%s\n", disk->dir, file->sub);
     149             :                                 }
     150             :                         }
     151             :                 }
     152             :         }
     153             : 
     154           0 :         if (found) {
     155           0 :                 log_fatal("\nInsufficient parity space. Data requires more parity than available.\n");
     156           0 :                 log_fatal("Move the 'outofparity' files to a larger disk.\n");
     157             :         }
     158             : }
     159             : 
     160         487 : void parity_size(struct snapraid_parity_handle* handle, data_off_t* out_size)
     161             : {
     162             :         unsigned s;
     163             :         data_off_t size;
     164             : 
     165             :         /* now compute the size summing all the parity splits */
     166         487 :         size = 0;
     167             : 
     168        2435 :         for (s = 0; s < handle->split_mac; ++s) {
     169        1948 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     170             : 
     171        1948 :                 size += split->size;
     172             :         }
     173             : 
     174         487 :         *out_size = size;
     175         487 : }
     176             : 
     177         609 : int parity_create(struct snapraid_parity_handle* handle, const struct snapraid_parity* parity, unsigned level, int mode, uint32_t block_size, data_off_t limit_size)
     178             : {
     179             :         unsigned s;
     180             :         data_off_t block_mask;
     181             : 
     182             :         /* mask of bits used by the block size */
     183         609 :         block_mask = ((data_off_t)block_size) - 1;
     184             : 
     185         609 :         handle->level = level;
     186         609 :         handle->split_mac = 0;
     187             : 
     188        3045 :         for (s = 0; s < parity->split_mac; ++s) {
     189        2436 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     190             :                 int ret;
     191             :                 int flags;
     192             : 
     193        2436 :                 advise_init(&split->advise, mode);
     194        2436 :                 pathcpy(split->path, sizeof(split->path), parity->split_map[s].path);
     195        2436 :                 split->size = parity->split_map[s].size;
     196        2436 :                 split->limit_size = PARITY_LIMIT(limit_size, s, level);
     197             : 
     198             :                 /* opening in sequential mode in Windows */
     199        2436 :                 flags = O_RDWR | O_CREAT | O_BINARY | advise_flags(&split->advise);
     200        2436 :                 split->f = open(split->path, flags, 0600);
     201        2436 :                 if (split->f == -1) {
     202             :                         /* LCOV_EXCL_START */
     203             :                         log_fatal("Error opening parity file '%s'. %s.\n", split->path, strerror(errno));
     204             :                         goto bail;
     205             :                         /* LCOV_EXCL_STOP */
     206             :                 }
     207             : 
     208             :                 /* we have a valid file handle */
     209        2436 :                 ++handle->split_mac;
     210             : 
     211             :                 /* get the stat info */
     212        2436 :                 ret = fstat(split->f, &split->st);
     213        2436 :                 if (ret != 0) {
     214             :                         /* LCOV_EXCL_START */
     215             :                         log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
     216             :                         goto bail;
     217             :                         /* LCOV_EXCL_STOP */
     218             :                 }
     219             : 
     220             :                 /* the initial valid size is the size on disk */
     221        2436 :                 split->valid_size = split->st.st_size;
     222             : 
     223             :                 /**
     224             :                  * If the parity size is not yet set, set it now.
     225             :                  * This happens when expanding the number of parities,
     226             :                  * or when upgrading from a content file that has not split->size data.
     227             :                  */
     228        2436 :                 if (split->size == PARITY_SIZE_INVALID) {
     229         144 :                         split->size = split->st.st_size;
     230             : 
     231             :                         /* ensure that the resulting size if block aligned */
     232         144 :                         if ((split->size & block_mask) != 0) {
     233             :                                 /* LCOV_EXCL_START */
     234             :                                 log_fatal("Error in preallocated size of parity file '%s' with size %" PRIu64 " and block %u .\n", split->path, split->size, block_size);
     235             :                                 goto bail;
     236             :                                 /* LCOV_EXCL_STOP */
     237             :                         }
     238             :                 }
     239             : 
     240        2436 :                 ret = advise_open(&split->advise, split->f);
     241        2436 :                 if (ret != 0) {
     242             :                         /* LCOV_EXCL_START */
     243             :                         log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     244             :                         goto bail;
     245             :                         /* LCOV_EXCL_STOP */
     246             :                 }
     247             :         }
     248             : 
     249         609 :         return 0;
     250             : 
     251           0 : bail:
     252             :         /* LCOV_EXCL_START */
     253             :         for (s = 0; s < handle->split_mac; ++s) {
     254             :                 struct snapraid_split_handle* split = &handle->split_map[s];
     255             :                 close(split->f);
     256             :                 split->f = -1;
     257             :         }
     258             :         return -1;
     259             :         /* LCOV_EXCL_STOP */
     260             : }
     261             : 
     262         719 : static int parity_handle_grow(struct snapraid_split_handle* split, data_off_t previous_size, data_off_t size, int skip_fallocate)
     263             : {
     264             :         int ret;
     265             : 
     266             :         (void)previous_size;
     267             : 
     268             :         /* simulate a failure for testing limits */
     269         719 :         if (split->limit_size != 0 && size > (data_off_t)split->limit_size)
     270         138 :                 return -1;
     271             : 
     272             : #if HAVE_FALLOCATE
     273         581 :         if (!skip_fallocate) {
     274             :                 /*
     275             :                  * Allocate real space using the specific Linux fallocate() operation.
     276             :                  * If the underline file-system doesn't support it, this operation fails.
     277             :                  *
     278             :                  * Instead posix_fallocate() fallbacks to write the whole file,
     279             :                  * and we cannot use it as we may need to initialize a multi terabyte
     280             :                  * file.
     281             :                  *
     282             :                  * See: fallocate vs posix_fallocate
     283             :                  * http://stackoverflow.com/questions/14063046/fallocate-vs-posix-fallocate
     284             :                  *
     285             :                  * To work better with Btrfs, use as offset the previous allocated size.
     286             :                  * Otherwise Btrfs will count as space needed even the already allocated one.
     287             :                  *
     288             :                  * See: Massive loss of disk space
     289             :                  * https://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg66454.html
     290             :                  */
     291         570 :                 ret = fallocate(split->f, 0, previous_size, size - previous_size);
     292             : 
     293             :                 /*
     294             :                  * In some legacy system fallocate() may return the error number
     295             :                  * as  positive integer, and in this case it doesn't set errno.
     296             :                  *
     297             :                  * Detect and handle this case.
     298             :                  *
     299             :                  * See: Fix fallocate error return on i386
     300             :                  * https://sourceware.org/ml/libc-hacker/2010-04/msg00000.html
     301             :                  *
     302             :                  * See: [PATCH XFS] Fix error return for fallocate() on XFS
     303             :                  * http://oss.sgi.com/archives/xfs/2009-11/msg00201.html
     304             :                  */
     305         570 :                 if (ret > 0) {
     306             :                         /* LCOV_EXCL_START */
     307             :                         errno = ret;
     308             :                         ret = -1;
     309             :                         /* LCOV_EXCL_STOP */
     310             :                 }
     311             :         } else {
     312          11 :                 errno = EOPNOTSUPP;
     313          11 :                 ret = -1;
     314             :         }
     315             : 
     316             :         /*
     317             :          * Fallback to ftruncate() if the operation is not supported.
     318             :          *
     319             :          * We get EOPNOTSUPP if the operation is not supported, like in ext3/ext2
     320             :          * or ENOSYS with kernel before 2.6.23, because fallocate is not supported
     321             :          * at all.
     322             :          *
     323             :          * See: man fallocate
     324             :          * ENOSYS - This kernel does not implement fallocate().
     325             :          * EOPNOTSUPP - The file system containing the file referred to by fd does not support this operation
     326             :          */
     327         581 :         if (ret != 0 && (errno == EOPNOTSUPP || errno == ENOSYS)) {
     328             :                 /* fallback using ftruncate() */
     329          11 :                 ret = ftruncate(split->f, size);
     330             :         }
     331             : #else
     332             :         (void)skip_fallocate; /* avoid the warning */
     333             : 
     334             :         /* allocate using a sparse file */
     335             :         ret = ftruncate(split->f, size);
     336             : #endif
     337             : 
     338         581 :         if (ret != 0)
     339           0 :                 log_tag("split:grow:%s:%" PRIu64 ": failed with error %s\n", split->path, size, strerror(errno));
     340             :         else
     341         581 :                 log_tag("split:grow:%s:%" PRIu64 ": ok\n", split->path, size);
     342             : 
     343         581 :         return ret;
     344             : }
     345             : 
     346         207 : static int parity_handle_shrink(struct snapraid_split_handle* split, data_off_t size)
     347             : {
     348             :         int ret;
     349             : 
     350         207 :         ret = ftruncate(split->f, size);
     351             : 
     352         207 :         if (ret != 0)
     353           0 :                 log_tag("split:shrink:%s:%" PRIu64 ": failed with error %s\n", split->path, size, strerror(errno));
     354             :         else
     355         207 :                 log_tag("split:shrink:%s:%" PRIu64 ": ok\n", split->path, size);
     356             : 
     357         207 :         return ret;
     358             : }
     359             : 
     360             : /**
     361             :  * Get the highest bit set.
     362             :  */
     363         719 : uint64_t hbit_u64(uint64_t v)
     364             : {
     365             :         unsigned ilog;
     366             : 
     367         719 :         ilog = 0;
     368       11692 :         while ((v /= 2) != 0)
     369       10973 :                 ++ilog;
     370             : 
     371         719 :         return 1ULL << ilog;
     372             : }
     373             : 
     374         132 : static int parity_handle_fill(struct snapraid_split_handle* split, data_off_t size, uint32_t block_size, int skip_fallocate, int skip_space_holder)
     375             : {
     376             :         data_off_t base;
     377             :         data_off_t delta;
     378             :         data_off_t block_mask;
     379             : 
     380             : #ifdef _WIN32
     381             :         /*
     382             :          * In Windows we want to avoid the annoying warning
     383             :          * message of disk full.
     384             :          *
     385             :          * To ensure to leave some space available, we first create
     386             :          * a spaceholder file >200 MB, to ensure to not fill completely
     387             :          * the disk.
     388             :          */
     389             :         char spaceholder_path[PATH_MAX];
     390             : 
     391             :         pathprint(spaceholder_path, sizeof(spaceholder_path), "%s%s", split->path, ".spaceholder");
     392             : 
     393             :         if (!skip_space_holder) {
     394             :                 data_off_t spaceholder_size = 256 * 1024 * 1024;
     395             :                 int spaceholder_f;
     396             : 
     397             :                 spaceholder_f = open(spaceholder_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);
     398             :                 if (spaceholder_f == -1) {
     399             :                         log_fatal("Failed to create space holder file '%s'.\n", spaceholder_path);
     400             :                         return -1;
     401             :                 }
     402             : 
     403             :                 /* note that in Windows ftruncate is really allocating space */
     404             :                 if (ftruncate(spaceholder_f, spaceholder_size) != 0) {
     405             :                         log_fatal("WARNING Failed to resize the space holder file '%s' to %" PRIu64 " bytes.\n", spaceholder_path, spaceholder_size);
     406             :                         log_fatal("Assuming that no more space is available.\n");
     407             :                         close(spaceholder_f);
     408             :                         remove(spaceholder_path);
     409             :                         return 0;
     410             :                 }
     411             : 
     412             :                 if (fsync(spaceholder_f) != 0) {
     413             :                         log_fatal("Failed to sync the space holder file '%s'.\n", spaceholder_path);
     414             :                         close(spaceholder_f);
     415             :                         remove(spaceholder_path);
     416             :                         return -1;
     417             :                 }
     418             : 
     419             :                 if (close(spaceholder_f) != 0) {
     420             :                         log_fatal("Failed to close the space holder file '%s'.\n", spaceholder_path);
     421             :                         remove(spaceholder_path);
     422             :                         return -1;
     423             :                 }
     424             :         }
     425             : #else
     426             :         (void)skip_space_holder;
     427             : #endif
     428             : 
     429             :         /* mask of bits used by the block size */
     430         132 :         block_mask = ((data_off_t)block_size) - 1;
     431             : 
     432             :         /* present size */
     433         132 :         base = split->st.st_size;
     434             : 
     435             :         /* truncate it to block size multiplier */
     436             :         /* in case of damage the size may get wrong */
     437         132 :         base &= ~block_mask;
     438             : 
     439             :         /* size we have to increase */
     440         132 :         delta = size - base;
     441             : 
     442         132 :         log_tag("split:fill:%s:%" PRIu64 ":%" PRIu64 ":\n", split->path, base, size);
     443             : 
     444             :         /* grow the size one bit at time, like a kind of binary search */
     445         851 :         while (delta != 0) {
     446             :                 int ret;
     447         719 :                 data_off_t run = hbit_u64(delta);
     448             : 
     449             :                 /* mask out the bit we process */
     450         719 :                 delta &= ~run;
     451             : 
     452         719 :                 log_tag("split:delta:%s:%" PRIu64 ":%" PRIu64 ":\n", split->path, base, run);
     453             : 
     454         719 :                 ret = parity_handle_grow(split, base, base + run, skip_fallocate);
     455         719 :                 if (ret != 0) {
     456             :                         /* we cannot grow, fallback enabling all the smaller bits */
     457         138 :                         delta = run - 1;
     458             : 
     459             :                         /* mask out the block size */
     460         138 :                         delta &= ~block_mask;
     461             :                 } else {
     462             :                         /* increase the effective size */
     463         581 :                         base += run;
     464             :                 }
     465             :         }
     466             : 
     467             :         /* ensure that the resulting size if block aligned */
     468         132 :         if ((base & block_mask) != 0) {
     469             :                 /* LCOV_EXCL_START */
     470             :                 log_fatal("Internal inconsistency in requested parity size %" PRIu64 " with block %u\n", base, block_size);
     471             :                 os_abort();
     472             :                 /* LCOV_EXCL_STOP */
     473             :         }
     474             : 
     475             : #ifdef _WIN32
     476             :         /* now delete the spaceholder file */
     477             :         if (remove(spaceholder_path) != 0) {
     478             :                 log_fatal("WARNING Failed to remove the space holder file '%s'.\n", spaceholder_path);
     479             :                 log_fatal("Continuing anyway.\n");
     480             :         }
     481             : #endif
     482             : 
     483             :         /* shrink to the expected size to ensure to throw away any extra */
     484             :         /* data allocated when the grow operation fails */
     485         132 :         return parity_handle_shrink(split, base);
     486             : }
     487             : 
     488        2412 : static int parity_handle_chsize(struct snapraid_split_handle* split, data_off_t size, uint32_t block_size, int skip_fallocate, int skip_space_holder)
     489             : {
     490             :         int ret;
     491             :         int f_ret;
     492             :         int f_errno;
     493             :         int f_dir;
     494             : 
     495        2412 :         if (split->st.st_size < size) {
     496         132 :                 f_ret = parity_handle_fill(split, size, block_size, skip_fallocate, skip_space_holder);
     497         132 :                 f_errno = errno;
     498         132 :                 f_dir = 1;
     499        2280 :         } else if (split->st.st_size > size) {
     500          75 :                 f_ret = parity_handle_shrink(split, size);
     501          75 :                 f_errno = errno;
     502          75 :                 f_dir = -1;
     503             :         } else {
     504        2205 :                 f_ret = 0;
     505        2205 :                 f_errno = 0;
     506        2205 :                 f_dir = 0;
     507             :         }
     508             : 
     509             :         /* get the stat info */
     510        2412 :         ret = fstat(split->f, &split->st);
     511        2412 :         if (ret != 0) {
     512             :                 /* LCOV_EXCL_START */
     513             :                 log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
     514             :                 return -1;
     515             :                 /* LCOV_EXCL_STOP */
     516             :         }
     517             : 
     518             :         /* now check the error */
     519        2412 :         if (f_ret != 0) {
     520             :                 /* LCOV_EXCL_START */
     521             :                 if (f_dir > 0) {
     522             :                         if (f_errno == ENOSPC) {
     523             :                                 log_fatal("Failed to grow parity file '%s' to size %" PRIu64 " due lack of space.\n", split->path, size);
     524             :                         } else {
     525             :                                 log_fatal("Error growing parity file '%s' to size %" PRIu64 ". Do you have enough space? %s.\n", split->path, size, strerror(f_errno));
     526             :                         }
     527             :                 } else {
     528             :                         log_fatal("Error truncating parity file '%s' to size %" PRIu64 ". %s.\n", split->path, size, strerror(f_errno));
     529             :                 }
     530             :                 return -1;
     531             :                 /* LCOV_EXCL_STOP */
     532             :         }
     533             : 
     534             :         /* if we shrink, update the valid size, but don't update when growing */
     535        2412 :         if (split->valid_size > split->st.st_size)
     536          75 :                 split->valid_size = split->st.st_size;
     537             : 
     538        2412 :         return 0;
     539             : }
     540             : 
     541        2412 : static int parity_split_is_fixed(struct snapraid_parity_handle* handle, unsigned s)
     542             : {
     543             :         /* next one */
     544        2412 :         ++s;
     545             : 
     546             :         /* the latest one is always growing */
     547        2412 :         if (s >= handle->split_mac)
     548         603 :                 return 0;
     549             : 
     550             :         /* if the next it's 0, this one is growing */
     551        1809 :         if (handle->split_map[s].size == 0)
     552        1222 :                 return 0;
     553             : 
     554         587 :         return 1;
     555             : }
     556             : 
     557         603 : int parity_chsize(struct snapraid_parity_handle* handle, struct snapraid_parity* parity, int* is_modified, data_off_t size, uint32_t block_size, int skip_fallocate, int skip_space_holder)
     558             : {
     559             :         int ret;
     560             :         unsigned s;
     561             :         data_off_t block_mask;
     562             : 
     563             :         /* mask of bits used by the block size */
     564         603 :         block_mask = ((data_off_t)block_size) - 1;
     565             : 
     566         603 :         if (size < 0) {
     567             :                 /* LCOV_EXCL_START */
     568             :                 return -1;
     569             :                 /* LCOV_EXCL_STOP */
     570             :         }
     571             : 
     572        3015 :         for (s = 0; s < handle->split_mac; ++s) {
     573        2412 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     574        2412 :                 int is_fixed = parity_split_is_fixed(handle, s);
     575             :                 data_off_t run;
     576             : 
     577        2412 :                 if (is_fixed) {
     578             :                         /* if the required size is smaller, we have to reduce also the file */
     579             :                         /* ignoring the previous size */
     580         587 :                         if (size <= split->size) {
     581             :                                 /* mark it as not fixed anymore for the later check */
     582           8 :                                 is_fixed = 0;
     583             : 
     584           8 :                                 run = size; /* allocate only the needed size */
     585             :                         } else {
     586             :                                 /* if the size cannot be changed, use the fixed one */
     587         579 :                                 run = split->size;
     588             : 
     589         579 :                                 if ((run & block_mask) != 0) {
     590             :                                         /* LCOV_EXCL_START */
     591             :                                         log_fatal("Internal inconsistency in split '%s' size with extra '%" PRIu64 "' bytes.\n", split->path, run & block_mask);
     592             :                                         return -1;
     593             :                                         /* LCOV_EXCL_STOP */
     594             :                                 }
     595             :                         }
     596             :                 } else {
     597             :                         /* otherwise tries to allocate all the needed remaining size */
     598        1825 :                         run = size;
     599             :                 }
     600             : 
     601        2412 :                 ret = parity_handle_chsize(split, run, block_size, skip_fallocate, skip_space_holder);
     602        2412 :                 if (ret != 0) {
     603             :                         /* LCOV_EXCL_START */
     604             :                         return -1;
     605             :                         /* LCOV_EXCL_STOP */
     606             :                 }
     607             : 
     608        2412 :                 if (split->st.st_size > run) {
     609             :                         /* LCOV_EXCL_START */
     610             :                         log_fatal("Unexpected over resizing parity file '%s' to size %" PRIu64 " resulting in size %" PRIu64 ".\n", split->path, run, (uint64_t)split->st.st_size);
     611             :                         return -1;
     612             :                         /* LCOV_EXCL_STOP */
     613        2412 :                 } else if (is_fixed && split->st.st_size < run) {
     614             :                         /* LCOV_EXCL_START */
     615             :                         log_fatal("Failed restoring parity file '%s' to size %" PRIu64 " resulting in size %" PRIu64 ".\n", split->path, run, (uint64_t)split->st.st_size);
     616             :                         return -1;
     617             :                         /* LCOV_EXCL_STOP */
     618             :                 } else {
     619             :                         /* here it's possible to get less than the requested size */
     620        2412 :                         run = split->st.st_size;
     621             : 
     622        2412 :                         if ((run & block_mask) != 0) {
     623             :                                 /* LCOV_EXCL_START */
     624             :                                 log_fatal("Internal inconsistency in final parity size %" PRIu64 " with block size %u\n", run, block_size);
     625             :                                 os_abort();
     626             :                                 /* LCOV_EXCL_STOP */
     627             :                         }
     628             : 
     629             :                         /* store what we have allocated */
     630        2412 :                         split->size = run;
     631             : 
     632             :                         /* decrease the remaining size */
     633        2412 :                         size -= run;
     634             :                 }
     635             :         }
     636             : 
     637             :         /* if we cannot allocate all the space */
     638         603 :         if (size != 0) {
     639             :                 /* LCOV_EXCL_START */
     640             :                 log_fatal("Failed to allocate all the required parity space. You miss %" PRIu64 " bytes.\n", size);
     641             :                 return -1;
     642             :                 /* LCOV_EXCL_STOP */
     643             :         }
     644             : 
     645             :         /* now copy the new size in the parity data */
     646         603 :         if (is_modified)
     647         481 :                 *is_modified = 0;
     648             : 
     649        3015 :         for (s = 0; s < handle->split_mac; ++s) {
     650        2412 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     651             : 
     652        2412 :                 if (parity->split_map[s].size != split->size) {
     653         320 :                         parity->split_map[s].size = split->size;
     654         320 :                         if (is_modified)
     655         264 :                                 *is_modified = 1;
     656             :                 }
     657             :         }
     658             : 
     659         603 :         return 0;
     660             : }
     661             : 
     662         420 : int parity_open(struct snapraid_parity_handle* handle, const struct snapraid_parity* parity, unsigned level, int mode, uint32_t block_size, data_off_t limit_size)
     663             : {
     664             :         unsigned s;
     665             :         data_off_t block_mask;
     666             : 
     667         420 :         handle->level = level;
     668         420 :         handle->split_mac = 0;
     669             : 
     670             :         /* mask of bits used by the block size */
     671         420 :         block_mask = ((data_off_t)block_size) - 1;
     672             : 
     673        2096 :         for (s = 0; s < parity->split_mac; ++s) {
     674        1677 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     675             :                 int ret;
     676             :                 int flags;
     677             : 
     678        1677 :                 advise_init(&split->advise, mode);
     679        1677 :                 pathcpy(split->path, sizeof(split->path), parity->split_map[s].path);
     680        1677 :                 split->size = parity->split_map[s].size;
     681        1677 :                 split->limit_size = PARITY_LIMIT(limit_size, s, level);
     682             : 
     683             :                 /* open for read */
     684             :                 /* O_NOATIME: do not change access time */
     685        1677 :                 flags = O_RDONLY | O_BINARY | advise_flags(&split->advise);
     686             : 
     687        1677 :                 split->f = open_noatime(split->path, flags);
     688        1677 :                 if (split->f == -1) {
     689             :                         /* LCOV_EXCL_START */
     690             :                         log_fatal("Error opening parity file '%s'. %s.\n", split->path, strerror(errno));
     691             :                         goto bail;
     692             :                         /* LCOV_EXCL_STOP */
     693             :                 }
     694             : 
     695             :                 /* we have a valid file handle */
     696        1676 :                 ++handle->split_mac;
     697             : 
     698             :                 /* get the stat info */
     699        1676 :                 ret = fstat(split->f, &split->st);
     700        1676 :                 if (ret != 0) {
     701             :                         /* LCOV_EXCL_START */
     702             :                         log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
     703             :                         goto bail;
     704             :                         /* LCOV_EXCL_STOP */
     705             :                 }
     706             : 
     707             :                 /* the initial valid size is the size on disk */
     708        1676 :                 split->valid_size = split->st.st_size;
     709             : 
     710             :                 /**
     711             :                  * If the parity size is not yet set, set it now.
     712             :                  * This happens when expanding the number of parities,
     713             :                  * or when upgrading from a content file that has not split->size data.
     714             :                  */
     715        1676 :                 if (split->size == PARITY_SIZE_INVALID) {
     716          40 :                         split->size = split->st.st_size;
     717             : 
     718             :                         /* ensure that the resulting size if block aligned */
     719          40 :                         if ((split->size & block_mask) != 0) {
     720             :                                 /* LCOV_EXCL_START */
     721             :                                 log_fatal("Error in preallocated size of parity file '%s' with size %" PRIu64 " and block %u .\n", split->path, split->size, block_size);
     722             :                                 goto bail;
     723             :                                 /* LCOV_EXCL_STOP */
     724             :                         }
     725             :                 }
     726             : 
     727        1676 :                 ret = advise_open(&split->advise, split->f);
     728        1676 :                 if (ret != 0) {
     729             :                         /* LCOV_EXCL_START */
     730             :                         log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     731             :                         goto bail;
     732             :                         /* LCOV_EXCL_STOP */
     733             :                 }
     734             :         }
     735             : 
     736         419 :         return 0;
     737             : 
     738           1 : bail:
     739             :         /* LCOV_EXCL_START */
     740             :         for (s = 0; s < handle->split_mac; ++s) {
     741             :                 struct snapraid_split_handle* split = &handle->split_map[s];
     742             :                 close(split->f);
     743             :                 split->f = -1;
     744             :         }
     745             :         return -1;
     746             :         /* LCOV_EXCL_STOP */
     747             : }
     748             : 
     749         469 : int parity_sync(struct snapraid_parity_handle* handle)
     750             : {
     751             : #if HAVE_FSYNC
     752             :         unsigned s;
     753             : 
     754        2345 :         for (s = 0; s < handle->split_mac; ++s) {
     755        1876 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     756             :                 int ret;
     757             : 
     758             :                 /* Ensure that data changes are written to disk. */
     759             :                 /* This is required to ensure that parity is more updated than content */
     760             :                 /* in case of a system crash. */
     761        1876 :                 ret = fsync(split->f);
     762        1876 :                 if (ret != 0) {
     763             :                         /* LCOV_EXCL_START */
     764             :                         log_fatal("Error syncing parity file '%s'. %s.\n", split->path, strerror(errno));
     765             :                         return -1;
     766             :                         /* LCOV_EXCL_STOP */
     767             :                 }
     768             :         }
     769             : #endif
     770             : 
     771         469 :         return 0;
     772             : }
     773             : 
     774         122 : int parity_truncate(struct snapraid_parity_handle* handle)
     775             : {
     776             :         unsigned s;
     777         122 :         int f_ret = 0;
     778             : 
     779         610 :         for (s = 0; s < handle->split_mac; ++s) {
     780         488 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     781             :                 int ret;
     782             : 
     783             :                 /* truncate any data that we know it's not valid */
     784         488 :                 ret = ftruncate(split->f, split->valid_size);
     785         488 :                 if (ret != 0) {
     786             :                         /* LCOV_EXCL_START */
     787             :                         log_fatal("Error truncating the parity file '%s' to size %" PRIu64 ". %s.\n", split->path, split->valid_size, strerror(errno));
     788             :                         f_ret = -1;
     789             :                         /* LCOV_EXCL_STOP */
     790             : 
     791             :                         /* continue to truncate the others */
     792             :                 }
     793             :         }
     794             : 
     795         122 :         return f_ret;
     796             : }
     797             : 
     798        1028 : int parity_close(struct snapraid_parity_handle* handle)
     799             : {
     800             :         unsigned s;
     801        1028 :         int f_ret = 0;
     802             : 
     803        5140 :         for (s = 0; s < handle->split_mac; ++s) {
     804        4112 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     805             :                 int ret;
     806             : 
     807        4112 :                 ret = close(split->f);
     808        4112 :                 if (ret != 0) {
     809             :                         /* LCOV_EXCL_START */
     810             :                         /* This is a serious error, as it may be the result of a failed write */
     811             :                         /* identified at later time. */
     812             :                         /* In a normal file-system (not NFS) it should never happen */
     813             :                         log_fatal("Error closing parity file '%s'. %s.\n", split->path, strerror(errno));
     814             :                         f_ret = -1;
     815             :                         /* LCOV_EXCL_STOP */
     816             : 
     817             :                         /* continue to close the others */
     818             :                 }
     819             : 
     820             :                 /* reset the descriptor */
     821        4112 :                 split->f = -1;
     822             :         }
     823             : 
     824        1028 :         return f_ret;
     825             : }
     826             : 
     827     4124881 : struct snapraid_split_handle* parity_split_find(struct snapraid_parity_handle* handle, data_off_t* offset)
     828             : {
     829             :         unsigned s;
     830             : 
     831     4124881 :         if (*offset < 0)
     832           0 :                 return 0;
     833             : 
     834     6512917 :         for (s = 0; s < handle->split_mac; ++s) {
     835     6501557 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     836             : 
     837     6501557 :                 if (*offset < split->size)
     838     4113521 :                         return split;
     839             : 
     840     2388036 :                 *offset -= split->size;
     841             :         }
     842             : 
     843       11360 :         return 0;
     844             : }
     845             : 
     846      606220 : int parity_write(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size)
     847             : {
     848             :         ssize_t write_ret;
     849             :         data_off_t offset;
     850             :         struct snapraid_split_handle* split;
     851             :         int ret;
     852             : 
     853      606220 :         offset = pos * (data_off_t)block_size;
     854             : 
     855      606220 :         split = parity_split_find(handle, &offset);
     856      606220 :         if (!split) {
     857             :                 /* LCOV_EXCL_START */
     858             :                 log_fatal("Writing parity data outside range at extra offset %" PRIu64 ".\n", offset);
     859             :                 return -1;
     860             :                 /* LCOV_EXCL_STOP */
     861             :         }
     862             : 
     863             :         /* update the valid range */
     864      606220 :         if (split->valid_size < offset + block_size)
     865      171013 :                 split->valid_size = offset + block_size;
     866             : 
     867      606220 :         bw_limit(handle->bw, block_size);
     868             : 
     869      606220 :         write_ret = pwrite(split->f, block_buffer, block_size, offset);
     870      606220 :         if (write_ret != (ssize_t)block_size) { /* conversion is safe because block_size is always small */
     871             :                 /* LCOV_EXCL_START */
     872             :                 if (errno == ENOSPC) {
     873             :                         log_fatal("Failed to grow parity file '%s' using write due lack of space.\n", split->path);
     874             :                 } else {
     875             :                         log_fatal("Error writing file '%s'. %s.\n", split->path, strerror(errno));
     876             :                 }
     877             :                 return -1;
     878             :                 /* LCOV_EXCL_STOP */
     879             :         }
     880             : 
     881      606220 :         ret = advise_write(&split->advise, split->f, offset, block_size);
     882      606220 :         if (ret != 0) {
     883             :                 /* LCOV_EXCL_START */
     884             :                 log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     885             :                 return -1;
     886             :                 /* LCOV_EXCL_STOP */
     887             :         }
     888             : 
     889      606220 :         return 0;
     890             : }
     891             : 
     892     3518661 : int parity_read(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size, fptr* out)
     893             : {
     894             :         ssize_t read_ret;
     895             :         data_off_t offset;
     896             :         unsigned count;
     897             :         struct snapraid_split_handle* split;
     898             :         int ret;
     899             : 
     900     3518661 :         offset = pos * (data_off_t)block_size;
     901             : 
     902     3518661 :         split = parity_split_find(handle, &offset);
     903     3518661 :         if (!split) {
     904             :                 /* LCOV_EXCL_START */
     905             :                 out("Reading parity data outside range at extra offset %" PRIu64 ".\n", offset);
     906             :                 return -1;
     907             :                 /* LCOV_EXCL_STOP */
     908             :         }
     909             : 
     910             :         /* if read is completely out of the valid range */
     911     3507301 :         if (offset >= split->valid_size) {
     912             :                 /* LCOV_EXCL_START */
     913             :                 out("Missing data reading file '%s' at offset %" PRIu64 " for size %u.\n", split->path, offset, block_size);
     914             :                 return -1;
     915             :                 /* LCOV_EXCL_STOP */
     916             :         }
     917             : 
     918     3428326 :         count = 0;
     919             :         do {
     920     3428326 :                 bw_limit(handle->bw, block_size - count);
     921             : 
     922     3428326 :                 read_ret = pread(split->f, block_buffer + count, block_size - count, offset + count);
     923     3428326 :                 if (read_ret < 0) {
     924             :                         /* LCOV_EXCL_START */
     925             :                         out("Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", split->path, offset + count, block_size - count, strerror(errno));
     926             :                         return -1;
     927             :                         /* LCOV_EXCL_STOP */
     928             :                 }
     929     3428326 :                 if (read_ret == 0) {
     930             :                         /* LCOV_EXCL_START */
     931             :                         out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", split->path, offset, strerror(errno));
     932             :                         return -1;
     933             :                         /* LCOV_EXCL_STOP */
     934             :                 }
     935             : 
     936     3428326 :                 count += read_ret;
     937     3428326 :         } while (count < block_size);
     938             : 
     939     3428326 :         ret = advise_read(&split->advise, split->f, offset, block_size);
     940     3428326 :         if (ret != 0) {
     941             :                 /* LCOV_EXCL_START */
     942             :                 out("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     943             :                 return -1;
     944             :                 /* LCOV_EXCL_STOP */
     945             :         }
     946             : 
     947     3428326 :         return block_size;
     948             : }
     949             : 

Generated by: LCOV version 1.0