Enforce BLOCK_MAX limits on both 32-bit and 64-bit builds

This commit is contained in:
Andrea Mazzoleni 2026-08-27 16:07:59 +02:00
parent 31718bdc4f
commit 237c9f0497
4 changed files with 67 additions and 26 deletions

View file

@ -468,12 +468,14 @@ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_
if (size % block_size != 0)
++blockmax;
if (blockmax > BLOCK_MAX) {
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / block_sizeof())) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", sub, blockmax);
#else
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large. Increase the block size.\n", sub, blockmax);
#endif
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_file));
file->sub = strdup_nofail(sub);
@ -501,12 +503,14 @@ struct snapraid_file* file_dup(struct snapraid_file* copy)
struct snapraid_file* file;
block_off_t i;
if (copy->blockmax > BLOCK_MAX) {
#if SIZE_MAX == UINT32_MAX
if (copy->blockmax > (block_off_t)(SIZE_MAX / block_sizeof())) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", copy->sub, copy->blockmax);
#else
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large. Increase the block size.\n", copy->sub, copy->blockmax);
#endif
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_file));
file->sub = strdup_nofail(copy->sub);
@ -872,12 +876,14 @@ struct snapraid_dealloc* dealloc_alloc(unsigned block_size, const char* sub, dat
if (size % block_size != 0)
++blockmax;
if (blockmax > BLOCK_MAX) {
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / BLOCK_HASH_SIZE)) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", sub, blockmax);
#else
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large. Increase the block size.\n", sub, blockmax);
#endif
exit(EXIT_FAILURE);
}
#endif
dealloc = malloc_nofail(sizeof(struct snapraid_dealloc));
dealloc->sub = strdup_nofail(sub);
@ -1519,14 +1525,16 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap
struct snapraid_extent* parity_extent;
struct snapraid_extent* file_extent;
#if SIZE_MAX == UINT32_MAX
if (parity_pos > (block_off_t)(SIZE_MAX - 4096)) {
if (parity_pos >= BLOCK_MAX) {
/* LCOV_EXCL_START */
#if SIZE_MAX == UINT32_MAX
log_fatal(ESOFT, "Parity position %" PRIu64 " is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", parity_pos);
#else
log_fatal(ESOFT, "Parity position %" PRIu64 " is too large. Increase the block size.\n", parity_pos);
#endif
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
fs_lock(disk);

View file

@ -16,6 +16,35 @@
/****************************************************************************/
/* snapraid */
/*
* Maximum number of block positions in a data/parity disk span.
*
* With the default 256 KiB block size, this limits the maximum supported
* size of the largest data/parity disk to:
*
* 32-bit: 2^27 blocks = 32 TiB
* 64-bit: 2^42 blocks = 1 EiB
*
* The limit scales linearly with the configured block size. For example,
* on 64-bit systems a 512 KiB block size allows 2 EiB per disk.
*
* This is an architectural sanity limit, not a memory-usage limit.
* SnapRAID stores per-block metadata in memory, so practical configurations
* will reach memory constraints long before BLOCK_MAX. The actual memory
* requirement depends on block size, hash size, number of data disks, and
* array layout.
*
* Based on historical HDD capacity growth, single disks are not expected
* to approach 1 EiB before approximately the year 2100. If necessary,
* larger disks can still be supported without changing BLOCK_MAX simply
* by increasing the configured block size.
*/
#if SIZE_MAX == UINT32_MAX
#define BLOCK_MAX (1ULL << 27)
#else
#define BLOCK_MAX (1ULL << 42)
#endif
/**
* Number of measures of the operation progress.
*/

View file

@ -55,12 +55,18 @@ static void import_file(struct snapraid_state* state, const char* path, uint64_t
if (size % block_size != 0)
++blockmax;
if (blockmax > BLOCK_MAX
#if SIZE_MAX == UINT32_MAX
|| blockmax > (block_off_t)(SIZE_MAX / sizeof(struct snapraid_import_block))
#endif
) {
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / sizeof(struct snapraid_import_block))) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", path, blockmax);
#else
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large. Increase the block size.\n", path, blockmax);
#endif
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_import_file));
file->path = strdup_nofail(path);
@ -173,12 +179,18 @@ static void import_dealloc(struct snapraid_state* state, const char* dir, struct
if (size % block_size != 0)
++blockmax;
if (blockmax > BLOCK_MAX
#if SIZE_MAX == UINT32_MAX
|| blockmax > (block_off_t)(SIZE_MAX / sizeof(struct snapraid_import_block))
#endif
) {
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / sizeof(struct snapraid_import_block))) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", path, blockmax);
#else
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large. Increase the block size.\n", path, blockmax);
#endif
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_import_file));
file->path = strdup_nofail(path);

View file

@ -2016,16 +2016,6 @@ static void state_content_check(struct snapraid_state* state, const char* path)
{
tommy_node* i;
#if SIZE_MAX == UINT32_MAX
block_off_t blockmax = parity_allocated_size(state);
if (blockmax > (block_off_t)(SIZE_MAX / sizeof(snapraid_info))) {
/* LCOV_EXCL_START */
log_fatal(ESOFT, "Array with %" PRIu64 " blocks in '%s' is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", blockmax, path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
/* check that any map has different name and position */
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
@ -3138,15 +3128,17 @@ static void state_read_content(struct snapraid_state* state, const char* path, S
os_abort();
/* LCOV_EXCL_STOP */
}
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / sizeof(snapraid_info))) {
if (blockmax > BLOCK_MAX) {
/* LCOV_EXCL_START */
decoding_error(path, f);
#if SIZE_MAX == UINT32_MAX
log_fatal(ESOFT, "Array with %" PRIu64 " blocks in '%s' is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", blockmax, path);
#else
log_fatal(ESOFT, "Array with %" PRIu64 " blocks in '%s' is too large. Increase the block size.\n", blockmax, path);
#endif
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
} else if (c == 'm' || c == 'M') {
struct snapraid_map* map;
char uuid[UUID_MAX];