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