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 :
|