diff --git a/cmdline/elem.c b/cmdline/elem.c index 87754d5d..0b43817d 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -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); diff --git a/cmdline/elem.h b/cmdline/elem.h index 72ac6cd5..7c6319ba 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -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. */ diff --git a/cmdline/import.c b/cmdline/import.c index 62b6cd03..d3631aef 100644 --- a/cmdline/import.c +++ b/cmdline/import.c @@ -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); diff --git a/cmdline/state.c b/cmdline/state.c index 4fbdd1f5..d3e08745 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -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];