LCOV - code coverage report
Current view: top level - cmdline - parity.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 232 254 91.3 %
Date: 2026-04-29 15:04:44 Functions: 18 19 94.7 %

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

Generated by: LCOV version 1.0