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