LCOV - code coverage report
Current view: top level - cmdline - parity.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 217 236 91.9 %
Date: 2017-11-06 22:14:04 Functions: 18 19 94.7 %

          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         642 : 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         642 :         parity_block = 0;
      42        4484 :         for (i = state->disklist; i != 0; i = i->next) {
      43        3842 :                 struct snapraid_disk* disk = i->data;
      44             : 
      45             :                 /* start from the declared size */
      46        3842 :                 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      190232 :                 while (block > parity_block && !block_has_file(fs_par2block_find(disk, block - 1)))
      52      182548 :                         --block;
      53             : 
      54             :                 /* get the highest value */
      55        3842 :                 if (block > parity_block)
      56        2267 :                         parity_block = block;
      57             :         }
      58             : 
      59         642 :         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      221719 :                 while (block > parity_block && !block_has_file_and_valid_parity(fs_par2block_find(disk, block - 1)))
      77      220677 :                         --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("\nYour data requires more parity than the available space.\n");
     156           0 :                 log_fatal("Please move the files 'outofparity' to another data 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         621 : 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         621 :         block_mask = ((data_off_t)block_size) - 1;
     184             : 
     185         621 :         handle->level = level;
     186         621 :         handle->split_mac = 0;
     187             : 
     188        3105 :         for (s = 0; s < parity->split_mac; ++s) {
     189        2484 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     190             :                 int ret;
     191             :                 int flags;
     192             : 
     193        2484 :                 advise_init(&split->advise, mode);
     194        2484 :                 pathcpy(split->path, sizeof(split->path), parity->split_map[s].path);
     195        2484 :                 split->size = parity->split_map[s].size;
     196        2484 :                 split->limit_size = PARITY_LIMIT(limit_size, s, level);
     197             : 
     198             :                 /* opening in sequential mode in Windows */
     199        2484 :                 flags = O_RDWR | O_CREAT | O_BINARY | advise_flags(&split->advise);
     200        2484 :                 split->f = open(split->path, flags, 0600);
     201        2484 :                 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        2484 :                 ++handle->split_mac;
     210             : 
     211             :                 /* get the stat info */
     212        2484 :                 ret = fstat(split->f, &split->st);
     213        2484 :                 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             :                 /**
     221             :                  * If the parity size is not yet set, set it now.
     222             :                  * This happens when expanding the number of parities,
     223             :                  * or when upgrading from a content file that has not split->size data.
     224             :                  */
     225        2484 :                 if (split->size == PARITY_SIZE_INVALID) {
     226         144 :                         split->size = split->st.st_size;
     227             : 
     228             :                         /* ensure that the resulting size if block aligned */
     229         144 :                         if ((split->size & block_mask) != 0) {
     230             :                                 /* LCOV_EXCL_START */
     231             :                                 log_fatal("Error in preallocated size of parity file '%s' with size %" PRIu64 " and block %u .\n", split->path, split->size, block_size);
     232             :                                 goto bail;
     233             :                                 /* LCOV_EXCL_STOP */
     234             :                         }
     235             :                 }
     236             : 
     237        2484 :                 ret = advise_open(&split->advise, split->f);
     238        2484 :                 if (ret != 0) {
     239             :                         /* LCOV_EXCL_START */
     240             :                         log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     241             :                         goto bail;
     242             :                         /* LCOV_EXCL_STOP */
     243             :                 }
     244             :         }
     245             : 
     246         621 :         return 0;
     247             : 
     248             : bail:
     249             :         /* LCOV_EXCL_START */
     250             :         for (s = 0; s < handle->split_mac; ++s) {
     251             :                 struct snapraid_split_handle* split = &handle->split_map[s];
     252             :                 close(split->f);
     253             :                 split->f = -1;
     254             :         }
     255             :         return -1;
     256             :         /* LCOV_EXCL_STOP */
     257             : }
     258             : 
     259         719 : static int parity_handle_grow(struct snapraid_split_handle* split, data_off_t size, int skip_fallocate)
     260             : {
     261             :         int ret;
     262             : 
     263             :         /* simulate a failure for testing limits */
     264         719 :         if (split->limit_size != 0 && size > (data_off_t)split->limit_size)
     265         138 :                 return -1;
     266             : 
     267             : #if HAVE_FALLOCATE
     268         581 :         if (!skip_fallocate) {
     269             :                 /*
     270             :                  * Allocate real space using the specific Linux fallocate() operation.
     271             :                  * If the underline file-system doesn't support it, this operation fails.
     272             :                  *
     273             :                  * Instead posix_fallocate() fallbacks to write the whole file,
     274             :                  * and we cannot use it as we may need to initialize a multi terabyte
     275             :                  * file.
     276             :                  *
     277             :                  * See: fallocate vs posix_fallocate
     278             :                  * http://stackoverflow.com/questions/14063046/fallocate-vs-posix-fallocate
     279             :                  */
     280         570 :                 ret = fallocate(split->f, 0, 0, size);
     281             : 
     282             :                 /*
     283             :                  * In some legacy system fallocate() may return the error number
     284             :                  * as  positive integer, and in this case it doesn't set errno.
     285             :                  *
     286             :                  * Detect and handle this case.
     287             :                  *
     288             :                  * See: Fix fallocate error return on i386
     289             :                  * https://sourceware.org/ml/libc-hacker/2010-04/msg00000.html
     290             :                  *
     291             :                  * See: [PATCH XFS] Fix error return for fallocate() on XFS
     292             :                  * http://oss.sgi.com/archives/xfs/2009-11/msg00201.html
     293             :                  */
     294         570 :                 if (ret > 0) {
     295             :                         /* LCOV_EXCL_START */
     296             :                         errno = ret;
     297             :                         ret = -1;
     298             :                         /* LCOV_EXCL_STOP */
     299             :                 }
     300             :         } else {
     301          11 :                 errno = EOPNOTSUPP;
     302          11 :                 ret = -1;
     303             :         }
     304             : 
     305             :         /*
     306             :          * Fallback to ftruncate() if the operation is not supported.
     307             :          *
     308             :          * We get EOPNOTSUPP if the operation is not supported, like in ext3/ext2
     309             :          * or ENOSYS with kernel before 2.6.23, because fallocate is not supported
     310             :          * at all.
     311             :          *
     312             :          * See: man fallocate
     313             :          * ENOSYS - This kernel does not implement fallocate().
     314             :          * EOPNOTSUPP - The file system containing the file referred to by fd does not support this operation
     315             :          */
     316         581 :         if (ret != 0 && (errno == EOPNOTSUPP || errno == ENOSYS)) {
     317             :                 /* fallback using ftruncate() */
     318          11 :                 ret = ftruncate(split->f, size);
     319             :         }
     320             : #else
     321             :         (void)skip_fallocate; /* avoid the warning */
     322             : 
     323             :         /* allocate using a sparse file */
     324             :         ret = ftruncate(split->f, size);
     325             : #endif
     326             : 
     327         581 :         return ret;
     328             : }
     329             : 
     330         214 : static int parity_handle_shrink(struct snapraid_split_handle* split, data_off_t size)
     331             : {
     332         214 :         return ftruncate(split->f, size);
     333             : }
     334             : 
     335             : /**
     336             :  * Get the highest bit set.
     337             :  */
     338         719 : uint64_t hbit_u64(uint64_t v)
     339             : {
     340             :         unsigned ilog;
     341             : 
     342         719 :         ilog = 0;
     343       12411 :         while ((v /= 2) != 0)
     344       10973 :                 ++ilog;
     345             : 
     346         719 :         return 1ULL << ilog;
     347             : }
     348             : 
     349         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)
     350             : {
     351             :         data_off_t base;
     352             :         data_off_t delta;
     353             :         data_off_t block_mask;
     354             : 
     355             : #ifdef _WIN32
     356             :         /*
     357             :          * In Windows we want to avoid the annoying warning
     358             :          * message of disk full.
     359             :          *
     360             :          * To ensure to leave some space available, we first create
     361             :          * a spaceholder file >200 MB, to ensure to not fill completely
     362             :          * the disk.
     363             :          */
     364             :         char spaceholder_path[PATH_MAX];
     365             : 
     366             :         pathprint(spaceholder_path, sizeof(spaceholder_path), "%s%s", split->path, ".spaceholder");
     367             : 
     368             :         if (!skip_space_holder) {
     369             :                 data_off_t spaceholder_size = 256 * 1024 * 1024;
     370             :                 int spaceholder_f;
     371             : 
     372             :                 spaceholder_f = open(spaceholder_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);
     373             :                 if (spaceholder_f == -1) {
     374             :                         log_fatal("Failed to create space holder file '%s'.\n", spaceholder_path);
     375             :                         return -1;
     376             :                 }
     377             : 
     378             :                 /* note that in Windows ftruncate is really allocating space */
     379             :                 if (ftruncate(spaceholder_f, spaceholder_size) != 0) {
     380             :                         log_fatal("WARNING Failed to resize the space holder file '%s' to %" PRIu64 " bytes.\n", spaceholder_path, spaceholder_size);
     381             :                         log_fatal("Assuming that no more space is available.\n");
     382             :                         close(spaceholder_f);
     383             :                         remove(spaceholder_path);
     384             :                         return 0;
     385             :                 }
     386             : 
     387             :                 if (fsync(spaceholder_f) != 0) {
     388             :                         log_fatal("Failed to sync the space holder file '%s'.\n", spaceholder_path);
     389             :                         close(spaceholder_f);
     390             :                         remove(spaceholder_path);
     391             :                         return -1;
     392             :                 }
     393             : 
     394             :                 if (close(spaceholder_f) != 0) {
     395             :                         log_fatal("Failed to close the space holder file '%s'.\n", spaceholder_path);
     396             :                         remove(spaceholder_path);
     397             :                         return -1;
     398             :                 }
     399             :         }
     400             : #else
     401             :         (void)skip_space_holder;
     402             : #endif
     403             : 
     404             :         /* mask of bits used by the block size */
     405         132 :         block_mask = ((data_off_t)block_size) - 1;
     406             : 
     407             :         /* present size */
     408         132 :         base = split->st.st_size;
     409             : 
     410             :         /* truncate it to block size multiplier */
     411             :         /* in case of damage the size may get wrong */
     412         132 :         base &= ~block_mask;
     413             : 
     414             :         /* size we have to increase */
     415         132 :         delta = size - base;
     416             : 
     417             :         /* grow the size one bit at time, like a kind of binary search */
     418         983 :         while (delta != 0) {
     419             :                 int ret;
     420         719 :                 data_off_t run = hbit_u64(delta);
     421             : 
     422             :                 /* mask out the bit we process */
     423         719 :                 delta &= ~run;
     424             : 
     425         719 :                 ret = parity_handle_grow(split, base + run, skip_fallocate);
     426         719 :                 if (ret != 0) {
     427             :                         /* we cannot grow, fallback enabling all the smaller bits */
     428         138 :                         delta = run - 1;
     429             : 
     430             :                         /* mask out the block size */
     431         138 :                         delta &= ~block_mask;
     432             :                 } else {
     433             :                         /* increase the effective size */
     434         581 :                         base += run;
     435             :                 }
     436             :         }
     437             : 
     438             :         /* ensure that the resulting size if block aligned */
     439         132 :         if ((base & block_mask) != 0) {
     440             :                 /* LCOV_EXCL_START */
     441             :                 log_fatal("Internal inconsistency in requested parity size %" PRIu64 " with block %u\n", base, block_size);
     442             :                 os_abort();
     443             :                 /* LCOV_EXCL_STOP */
     444             :         }
     445             : 
     446             : #ifdef _WIN32
     447             :         /* now delete the spaceholder file */
     448             :         if (remove(spaceholder_path) != 0) {
     449             :                 log_fatal("WARNING Failed to remove the space holder file '%s'.\n", spaceholder_path);
     450             :                 log_fatal("Continuing anyway.\n");
     451             :         }
     452             : #endif
     453             : 
     454             :         /* shrink to the expected size to ensure to throw away any extra */
     455             :         /* data allocated when the grow operation fails */
     456         132 :         return parity_handle_shrink(split, base);
     457             : }
     458             : 
     459        2460 : 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)
     460             : {
     461             :         int ret;
     462             :         int f_ret;
     463             :         int f_errno;
     464             :         int f_dir;
     465             : 
     466        2460 :         if (split->st.st_size < size) {
     467         132 :                 f_ret = parity_handle_fill(split, size, block_size, skip_fallocate, skip_space_holder);
     468         132 :                 f_errno = errno;
     469         132 :                 f_dir = 1;
     470        2328 :         } else if (split->st.st_size > size) {
     471          82 :                 f_ret = parity_handle_shrink(split, size);
     472          82 :                 f_errno = errno;
     473          82 :                 f_dir = -1;
     474             :         } else {
     475        2246 :                 f_ret = 0;
     476        2246 :                 f_errno = 0;
     477        2246 :                 f_dir = 0;
     478             :         }
     479             : 
     480             :         /* get the stat info */
     481        2460 :         ret = fstat(split->f, &split->st);
     482        2460 :         if (ret != 0) {
     483             :                 /* LCOV_EXCL_START */
     484             :                 log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
     485             :                 return -1;
     486             :                 /* LCOV_EXCL_STOP */
     487             :         }
     488             : 
     489             :         /* now check the error */
     490        2460 :         if (f_ret != 0) {
     491             :                 /* LCOV_EXCL_START */
     492             :                 if (f_dir > 0) {
     493             :                         if (f_errno == ENOSPC) {
     494             :                                 log_fatal("Failed to grow parity file '%s' to size %" PRIu64 " due lack of space.\n", split->path, size);
     495             :                         } else {
     496             :                                 log_fatal("Error growing parity file '%s' to size %" PRIu64 ". Do you have enough space? %s.\n", split->path, size, strerror(f_errno));
     497             :                         }
     498             :                 } else {
     499             :                         log_fatal("Error truncating parity file '%s' to size %" PRIu64 ". %s.\n", split->path, size, strerror(f_errno));
     500             :                 }
     501             :                 return -1;
     502             :                 /* LCOV_EXCL_STOP */
     503             :         }
     504             : 
     505        2460 :         return 0;
     506             : }
     507             : 
     508        2460 : static int parity_split_is_fixed(struct snapraid_parity_handle* handle, unsigned s)
     509             : {
     510             :         /* next one */
     511        2460 :         ++s;
     512             : 
     513             :         /* the latest one is always growing */
     514        2460 :         if (s >= handle->split_mac)
     515         615 :                 return 0;
     516             : 
     517             :         /* if the next it's 0, this one is growing */
     518        1845 :         if (handle->split_map[s].size == 0)
     519        1234 :                 return 0;
     520             : 
     521         611 :         return 1;
     522             : }
     523             : 
     524         615 : 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)
     525             : {
     526             :         int ret;
     527             :         unsigned s;
     528             :         data_off_t block_mask;
     529             : 
     530             :         /* mask of bits used by the block size */
     531         615 :         block_mask = ((data_off_t)block_size) - 1;
     532             : 
     533         615 :         if (size < 0) {
     534             :                 /* LCOV_EXCL_START */
     535             :                 return -1;
     536             :                 /* LCOV_EXCL_STOP */
     537             :         }
     538             : 
     539        3075 :         for (s = 0; s < handle->split_mac; ++s) {
     540        2460 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     541        2460 :                 int is_fixed = parity_split_is_fixed(handle, s);
     542             :                 data_off_t run;
     543             : 
     544        2460 :                 if (is_fixed) {
     545             :                         /* if the required size is smaller, we have to reduce also the file */
     546             :                         /* ignoring the previous size */
     547         611 :                         if (size <= split->size) {
     548             :                                 /* mark it as not fixed anymore for the later check */
     549          10 :                                 is_fixed = 0;
     550             : 
     551          10 :                                 run = size; /* allocate only the needed size */
     552             :                         } else {
     553             :                                 /* if the size cannot be changed, use the fixed one */
     554         601 :                                 run = split->size;
     555             : 
     556         601 :                                 if ((run & block_mask) != 0) {
     557             :                                         /* LCOV_EXCL_START */
     558             :                                         log_fatal("Internal inconsistency in split '%s' size with extra '%" PRIu64 "' bytes.\n", split->path, run & block_mask);
     559             :                                         return -1;
     560             :                                         /* LCOV_EXCL_STOP */
     561             :                                 }
     562             :                         }
     563             :                 } else {
     564             :                         /* otherwise tries to allocate all the needed remaining size */
     565        1849 :                         run = size;
     566             :                 }
     567             : 
     568        2460 :                 ret = parity_handle_chsize(split, run, block_size, skip_fallocate, skip_space_holder);
     569        2460 :                 if (ret != 0) {
     570             :                         /* LCOV_EXCL_START */
     571             :                         return -1;
     572             :                         /* LCOV_EXCL_STOP */
     573             :                 }
     574             : 
     575        2460 :                 if (split->st.st_size > run) {
     576             :                         /* LCOV_EXCL_START */
     577             :                         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);
     578             :                         return -1;
     579             :                         /* LCOV_EXCL_STOP */
     580        2460 :                 } else if (is_fixed && split->st.st_size < run) {
     581             :                         /* LCOV_EXCL_START */
     582             :                         log_fatal("Failed restoring parity file '%s' to size %" PRIu64 " resulting in size %" PRIu64 ".\n", split->path, run, (uint64_t)split->st.st_size);
     583             :                         return -1;
     584             :                         /* LCOV_EXCL_STOP */
     585             :                 } else {
     586             :                         /* here it's possible to get less than the requested size */
     587        2460 :                         run = split->st.st_size;
     588             : 
     589        2460 :                         if ((run & block_mask) != 0) {
     590             :                                 /* LCOV_EXCL_START */
     591             :                                 log_fatal("Internal inconsistency in final parity size %" PRIu64 " with block size %u\n", run, block_size);
     592             :                                 os_abort();
     593             :                                 /* LCOV_EXCL_STOP */
     594             :                         }
     595             : 
     596             :                         /* store what we have allocated */
     597        2460 :                         split->size = run;
     598             : 
     599             :                         /* decrease the remaining size */
     600        2460 :                         size -= run;
     601             :                 }
     602             :         }
     603             : 
     604             :         /* if we cannot allocate all the space */
     605         615 :         if (size != 0) {
     606             :                 /* LCOV_EXCL_START */
     607             :                 log_fatal("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         615 :         if (is_modified)
     614         481 :                 *is_modified = 0;
     615             : 
     616        3075 :         for (s = 0; s < handle->split_mac; ++s) {
     617        2460 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     618             : 
     619        2460 :                 if (parity->split_map[s].size != split->size) {
     620         320 :                         parity->split_map[s].size = split->size;
     621         320 :                         if (is_modified)
     622         264 :                                 *is_modified = 1;
     623             :                 }
     624             :         }
     625             : 
     626         615 :         return 0;
     627             : }
     628             : 
     629         402 : 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         402 :         handle->level = level;
     635         402 :         handle->split_mac = 0;
     636             : 
     637             :         /* mask of bits used by the block size */
     638         402 :         block_mask = ((data_off_t)block_size) - 1;
     639             : 
     640        2006 :         for (s = 0; s < parity->split_mac; ++s) {
     641        1605 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     642             :                 int ret;
     643             :                 int flags;
     644             : 
     645        1605 :                 advise_init(&split->advise, mode);
     646        1605 :                 pathcpy(split->path, sizeof(split->path), parity->split_map[s].path);
     647        1605 :                 split->size = parity->split_map[s].size;
     648        1605 :                 split->limit_size = PARITY_LIMIT(limit_size, s, level);
     649             : 
     650             :                 /* open for read */
     651             :                 /* O_NOATIME: do not change access time */
     652        1605 :                 flags = O_RDONLY | O_BINARY | advise_flags(&split->advise);
     653             : 
     654        1605 :                 split->f = open_noatime(split->path, flags);
     655        1605 :                 if (split->f == -1) {
     656             :                         /* LCOV_EXCL_START */
     657             :                         log_fatal("Error opening parity file '%s'. %s.\n", split->path, strerror(errno));
     658             :                         goto bail;
     659             :                         /* LCOV_EXCL_STOP */
     660             :                 }
     661             : 
     662             :                 /* we have a valid file handle */
     663        1604 :                 ++handle->split_mac;
     664             : 
     665             :                 /* get the stat info */
     666        1604 :                 ret = fstat(split->f, &split->st);
     667        1604 :                 if (ret != 0) {
     668             :                         /* LCOV_EXCL_START */
     669             :                         log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
     670             :                         goto bail;
     671             :                         /* LCOV_EXCL_STOP */
     672             :                 }
     673             : 
     674             :                 /**
     675             :                  * If the parity size is not yet set, set it now.
     676             :                  * This happens when expanding the number of parities,
     677             :                  * or when upgrading from a content file that has not split->size data.
     678             :                  */
     679        1604 :                 if (split->size == PARITY_SIZE_INVALID) {
     680          40 :                         split->size = split->st.st_size;
     681             : 
     682             :                         /* ensure that the resulting size if block aligned */
     683          40 :                         if ((split->size & block_mask) != 0) {
     684             :                                 /* LCOV_EXCL_START */
     685             :                                 log_fatal("Error in preallocated size of parity file '%s' with size %" PRIu64 " and block %u .\n", split->path, split->size, block_size);
     686             :                                 goto bail;
     687             :                                 /* LCOV_EXCL_STOP */
     688             :                         }
     689             :                 }
     690             : 
     691        1604 :                 ret = advise_open(&split->advise, split->f);
     692        1604 :                 if (ret != 0) {
     693             :                         /* LCOV_EXCL_START */
     694             :                         log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     695             :                         goto bail;
     696             :                         /* LCOV_EXCL_STOP */
     697             :                 }
     698             :         }
     699             : 
     700         401 :         return 0;
     701             : 
     702             : bail:
     703             :         /* LCOV_EXCL_START */
     704             :         for (s = 0; s < handle->split_mac; ++s) {
     705             :                 struct snapraid_split_handle* split = &handle->split_map[s];
     706             :                 close(split->f);
     707             :                 split->f = -1;
     708             :         }
     709             :         return -1;
     710             :         /* LCOV_EXCL_STOP */
     711             : }
     712             : 
     713         469 : int parity_sync(struct snapraid_parity_handle* handle)
     714             : {
     715             : #if HAVE_FSYNC
     716             :         unsigned s;
     717             : 
     718        2345 :         for (s = 0; s < handle->split_mac; ++s) {
     719        1876 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     720             :                 int ret;
     721             : 
     722             :                 /* Ensure that data changes are written to disk. */
     723             :                 /* This is required to ensure that parity is more updated than content */
     724             :                 /* in case of a system crash. */
     725        1876 :                 ret = fsync(split->f);
     726        1876 :                 if (ret != 0) {
     727             :                         /* LCOV_EXCL_START */
     728             :                         log_fatal("Error synching parity file '%s'. %s.\n", split->path, strerror(errno));
     729             :                         return -1;
     730             :                         /* LCOV_EXCL_STOP */
     731             :                 }
     732             :         }
     733             : #endif
     734             : 
     735         469 :         return 0;
     736             : }
     737             : 
     738        1022 : int parity_close(struct snapraid_parity_handle* handle)
     739             : {
     740             :         unsigned s;
     741        1022 :         int f_ret = 0;
     742             : 
     743        5110 :         for (s = 0; s < handle->split_mac; ++s) {
     744        4088 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     745             :                 int ret;
     746             : 
     747        4088 :                 ret = close(split->f);
     748        4088 :                 if (ret != 0) {
     749             :                         /* LCOV_EXCL_START */
     750             :                         /* This is a serious error, as it may be the result of a failed write */
     751             :                         /* identified at later time. */
     752             :                         /* In a normal file-system (not NFS) it should never happen */
     753             :                         log_fatal("Error closing parity file '%s'. %s.\n", split->path, strerror(errno));
     754             :                         f_ret = -1;
     755             :                         /* LCOV_EXCL_STOP */
     756             : 
     757             :                         /* continue to close the others */
     758             :                 }
     759             : 
     760             :                 /* reset the descriptor */
     761        4088 :                 split->f = -1;
     762             :         }
     763             : 
     764        1022 :         return f_ret;
     765             : }
     766             : 
     767     4111987 : struct snapraid_split_handle* parity_split_find(struct snapraid_parity_handle* handle, data_off_t* offset)
     768             : {
     769             :         unsigned s;
     770             : 
     771     4111987 :         if (*offset < 0)
     772           0 :                 return 0;
     773             : 
     774     6499462 :         for (s = 0; s < handle->split_mac; ++s) {
     775     6488102 :                 struct snapraid_split_handle* split = &handle->split_map[s];
     776             : 
     777     6488102 :                 if (*offset < split->size)
     778     4100627 :                         return split;
     779             : 
     780     2387475 :                 *offset -= split->size;
     781             :         }
     782             : 
     783       11360 :         return 0;
     784             : }
     785             : 
     786      610499 : int parity_write(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size)
     787             : {
     788             :         ssize_t write_ret;
     789             :         data_off_t offset;
     790             :         struct snapraid_split_handle* split;
     791             :         int ret;
     792             : 
     793      610499 :         offset = pos * (data_off_t)block_size;
     794             : 
     795      610499 :         split = parity_split_find(handle, &offset);
     796      611049 :         if (!split) {
     797             :                 /* LCOV_EXCL_START */
     798             :                 log_fatal("Writing parity data outside range at extra offset %" PRIu64 ".\n", offset);
     799             :                 return -1;
     800             :                 /* LCOV_EXCL_STOP */
     801             :         }
     802             : 
     803      611049 :         write_ret = pwrite(split->f, block_buffer, block_size, offset);
     804      606938 :         if (write_ret != (ssize_t)block_size) { /* conversion is safe because block_size is always small */
     805             :                 /* LCOV_EXCL_START */
     806             :                 if (errno == ENOSPC) {
     807             :                         log_fatal("Failed to grow parity file '%s' using write due lack of space.\n", split->path);
     808             :                 } else {
     809             :                         log_fatal("Error writing file '%s'. %s.\n", split->path, strerror(errno));
     810             :                 }
     811             :                 return -1;
     812             :                 /* LCOV_EXCL_STOP */
     813             :         }
     814             : 
     815      606938 :         ret = advise_write(&split->advise, split->f, offset, block_size);
     816      607802 :         if (ret != 0) {
     817             :                 /* LCOV_EXCL_START */
     818             :                 log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     819             :                 return -1;
     820             :                 /* LCOV_EXCL_STOP */
     821             :         }
     822             : 
     823      607802 :         return 0;
     824             : }
     825             : 
     826     3501702 : int parity_read(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size, fptr* out)
     827             : {
     828             :         ssize_t read_ret;
     829             :         data_off_t offset;
     830             :         unsigned count;
     831             :         struct snapraid_split_handle* split;
     832             :         int ret;
     833             : 
     834     3501702 :         offset = pos * (data_off_t)block_size;
     835             : 
     836     3501702 :         split = parity_split_find(handle, &offset);
     837     3501739 :         if (!split) {
     838             :                 /* LCOV_EXCL_START */
     839             :                 out("Reading parity data outside range at extra offset %" PRIu64 ".\n", offset);
     840             :                 return -1;
     841             :                 /* LCOV_EXCL_STOP */
     842             :         }
     843             : 
     844     3490379 :         count = 0;
     845             :         do {
     846     3490327 :                 read_ret = pread(split->f, block_buffer + count, block_size - count, offset + count);
     847     3490305 :                 if (read_ret < 0) {
     848             :                         /* LCOV_EXCL_START */
     849             :                         out("Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", split->path, offset + count, block_size - count, strerror(errno));
     850             :                         return -1;
     851             :                         /* LCOV_EXCL_STOP */
     852             :                 }
     853     3490305 :                 if (read_ret == 0) {
     854             :                         /* LCOV_EXCL_START */
     855             :                         out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", split->path, offset, strerror(errno));
     856             :                         return -1;
     857             :                         /* LCOV_EXCL_STOP */
     858             :                 }
     859             : 
     860     3490305 :                 count += read_ret;
     861     3490305 :         } while (count < block_size);
     862             : 
     863     3490357 :         ret = advise_read(&split->advise, split->f, offset, block_size);
     864     3490309 :         if (ret != 0) {
     865             :                 /* LCOV_EXCL_START */
     866             :                 out("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
     867             :                 return -1;
     868             :                 /* LCOV_EXCL_STOP */
     869             :         }
     870             : 
     871     3490309 :         return block_size;
     872             : }
     873             : 

Generated by: LCOV version 1.13