mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Detect truncated parity using the contiguous valid physical prefix
This commit is contained in:
parent
ef6fa54ee2
commit
4423c8c895
4 changed files with 95 additions and 11 deletions
|
|
@ -127,6 +127,32 @@ void parity_size(struct snapraid_parity_handle* handle, data_off_t* out_size)
|
|||
*out_size = size;
|
||||
}
|
||||
|
||||
void parity_valid_size(struct snapraid_parity_handle* handle, data_off_t* out_size)
|
||||
{
|
||||
unsigned s;
|
||||
data_off_t size;
|
||||
|
||||
/* compute the contiguous valid prefix of the logical parity layout */
|
||||
size = 0;
|
||||
|
||||
for (s = 0; s < handle->split_mac; ++s) {
|
||||
struct snapraid_split_handle* split = &handle->split_map[s];
|
||||
data_off_t run = split->valid_size;
|
||||
|
||||
/* don't count physical data outside the logical split */
|
||||
if (run > split->size)
|
||||
run = split->size;
|
||||
|
||||
size += run;
|
||||
|
||||
/* later splits cannot compensate for a hole in this one */
|
||||
if (run < split->size)
|
||||
break;
|
||||
}
|
||||
|
||||
*out_size = size;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned s;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,15 @@ int parity_chsize(struct snapraid_parity_handle* handle, struct snapraid_parity*
|
|||
*/
|
||||
void parity_size(struct snapraid_parity_handle* handle, data_off_t* out_size);
|
||||
|
||||
/**
|
||||
* Get the size of the contiguous valid prefix of the parity.
|
||||
*
|
||||
* This returns the logical prefix covered by valid data in all consecutive splits.
|
||||
* For example, with two 100 GiB splits, if the first is truncated to 90 GiB
|
||||
* and the second is fully valid, the valid prefix is 90 GiB, not 190 GiB.
|
||||
*/
|
||||
void parity_valid_size(struct snapraid_parity_handle* handle, data_off_t* out_size);
|
||||
|
||||
/**
|
||||
* Open an already existing parity file.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "state.h"
|
||||
#include "support.h"
|
||||
#include "stream.h"
|
||||
#include "parity.h"
|
||||
#include "tommyds/tommyhash.h"
|
||||
#include "tommyds/tommyarray.h"
|
||||
#include "tommyds/tommyarrayblkof.h"
|
||||
|
|
@ -297,6 +298,47 @@ static void test_crc32c(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void test_parity(void)
|
||||
{
|
||||
static const struct {
|
||||
unsigned split_mac;
|
||||
data_off_t size[2];
|
||||
data_off_t valid_size[2];
|
||||
data_off_t expected;
|
||||
} test[] = {
|
||||
{ 1, { 100, 0 }, { 100, 0 }, 100 },
|
||||
{ 1, { 100, 0 }, { 90, 0 }, 90 },
|
||||
{ 1, { 100, 0 }, { 99, 0 }, 99 },
|
||||
{ 2, { 100, 100 }, { 90, 100 }, 90 },
|
||||
{ 2, { 100, 100 }, { 100, 70 }, 170 },
|
||||
{ 2, { 100, 100 }, { 110, 100 }, 200 },
|
||||
{ 2, { 100, 100 }, { 0, 100 }, 0 },
|
||||
{ 0, { 0, 0 }, { 0, 0 }, 0 }
|
||||
};
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; test[i].split_mac != 0; ++i) {
|
||||
struct snapraid_parity_handle handle;
|
||||
data_off_t size;
|
||||
unsigned s;
|
||||
|
||||
memset(&handle, 0, sizeof(handle));
|
||||
handle.split_mac = test[i].split_mac;
|
||||
for (s = 0; s < handle.split_mac; ++s) {
|
||||
handle.split_map[s].size = test[i].size[s];
|
||||
handle.split_map[s].valid_size = test[i].valid_size[s];
|
||||
}
|
||||
|
||||
parity_valid_size(&handle, &size);
|
||||
if (size != test[i].expected) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed parity valid size test\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of tommy data structures.
|
||||
*/
|
||||
|
|
@ -1079,6 +1121,7 @@ void selftest(void)
|
|||
}
|
||||
test_hash();
|
||||
test_crc32c();
|
||||
test_parity();
|
||||
test_tommy();
|
||||
test_stream();
|
||||
test_wnmatch();
|
||||
|
|
|
|||
|
|
@ -1525,7 +1525,8 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
{
|
||||
block_off_t blockmax;
|
||||
block_off_t used_paritymax;
|
||||
block_off_t file_paritymax;
|
||||
data_off_t used_parity_size;
|
||||
data_off_t file_parity_size;
|
||||
data_off_t size;
|
||||
int ret;
|
||||
struct snapraid_parity_handle parity_handle[LEV_MAX];
|
||||
|
|
@ -1540,9 +1541,10 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
|
||||
/* minimum size of the parity files we expect */
|
||||
used_paritymax = parity_used_size(state);
|
||||
used_parity_size = used_paritymax * (data_off_t)state->block_size;
|
||||
|
||||
/* effective size of the parity files */
|
||||
file_paritymax = 0;
|
||||
file_parity_size = 0;
|
||||
|
||||
if (blockstart > blockmax) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1570,26 +1572,30 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
}
|
||||
|
||||
/* number of block in the parity file */
|
||||
parity_size(&parity_handle[l], &out_size);
|
||||
parity_valid_size(&parity_handle[l], &out_size);
|
||||
parityblocks = out_size / state->block_size;
|
||||
|
||||
/* if the file is too small */
|
||||
if (parityblocks < used_paritymax) {
|
||||
if (out_size < used_parity_size) {
|
||||
log_fatal(ESOFT, "WARNING! The %s parity has only %" PRIu64 " blocks instead of %" PRIu64 ".\n", lev_name(l), parityblocks, used_paritymax);
|
||||
}
|
||||
|
||||
/* keep the smallest parity number of blocks */
|
||||
if (l == 0 || file_paritymax > parityblocks)
|
||||
file_paritymax = parityblocks;
|
||||
/* keep the smallest valid parity size */
|
||||
if (l == 0 || file_parity_size > out_size)
|
||||
file_parity_size = out_size;
|
||||
}
|
||||
|
||||
/* if we do a full parity realloc or computation, having a wrong parity size is expected */
|
||||
if (!state->opt.force_realloc && !state->opt.force_full) {
|
||||
/*
|
||||
* A full parity rebuild or reallocation from zero reconstructs truncated
|
||||
* parity. Tail-only reallocation still relies on the existing prefix, so a
|
||||
* truncation before parity_tail remains fatal.
|
||||
*/
|
||||
if (!state->opt.force_full && !(state->opt.force_realloc && state->opt.parity_tail == 0)) {
|
||||
/* if the parities are too small */
|
||||
if (file_paritymax < used_paritymax) {
|
||||
if (file_parity_size < used_parity_size) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(ESOFT, "DANGER! One or more the parity files are smaller than expected!\n");
|
||||
if (file_paritymax != 0) {
|
||||
if (file_parity_size != 0) {
|
||||
log_fatal(ESOFT, "If this happens because you are using an old content file,\n");
|
||||
log_fatal(ESOFT, "you can 'sync' anyway using 'snapraid --force-full sync'\n");
|
||||
log_fatal(ESOFT, "to force a full rebuild of the parity.\n");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue