From 462482d319033d8d031c32ea038f0970b2893090 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sat, 4 Jul 2026 21:37:22 +0300 Subject: [PATCH] Fix zero-padded big_write checksum verification in the new store --- src/blockstore/blockstore_heap.cpp | 3 +- src/test/test_blockstore.cpp | 45 ++++++++++++++++++++++++++++ src/test/test_heap.cpp | 47 ++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/blockstore/blockstore_heap.cpp b/src/blockstore/blockstore_heap.cpp index c9fb787b..bd7257fa 100644 --- a/src/blockstore/blockstore_heap.cpp +++ b/src/blockstore/blockstore_heap.cpp @@ -1139,6 +1139,7 @@ bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *bit { bool res = true; uint32_t pos = start; + uint32_t block_start = (start/dsk->csum_block_size)*dsk->csum_block_size; uint32_t block_end = (start/dsk->csum_block_size + 1)*dsk->csum_block_size; uint32_t block_crc = 0; bool isset = false; @@ -1153,7 +1154,7 @@ bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *bit while (pos < end && pos < block_end && !(bitmap[pos/dsk->bitmap_granularity/8] & (1 << ((pos/dsk->bitmap_granularity) % 8)))) pos += dsk->bitmap_granularity; // zero padding at the beginning or at the end of the block is not counted - if (pos > prev && prev > 0 && pos < block_end) + if (pos > prev && prev > block_start && pos < block_end) block_crc = crc32c_pad(block_crc, NULL, 0, pos-prev, 0); prev = pos; while (pos < end && pos < block_end && (bitmap[pos/dsk->bitmap_granularity/8] & (1 << ((pos/dsk->bitmap_granularity) % 8)))) diff --git a/src/test/test_blockstore.cpp b/src/test/test_blockstore.cpp index 025078d0..b7df70cc 100644 --- a/src/test/test_blockstore.cpp +++ b/src/test/test_blockstore.cpp @@ -834,6 +834,50 @@ static void test_fsync_batch_big() free(op2.buf); } +static void test_padded_csum_sparse_leading_hole() +{ + printf("\n-- test_padded_csum_sparse_leading_hole\n"); + + bs_test_t test; + test.default_cfg(); + test.config["csum_block_size"] = "16384"; + test.init(); + + // Initial write at offset=20K, len=4K on a fresh object. + // The csum block is [16K..32K); granule [16K..20K) is a leading hole. + printf("writing\n"); + blockstore_op_t op; + op.opcode = BS_OP_WRITE_STABLE; + op.oid = { .inode = 1, .stripe = 0 }; + op.version = 1; + op.offset = 20*1024; + op.len = 4096; + op.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, 4096); + memset(op.buf, 0xaa, 4096); + test.exec_op(&op); + assert(op.retval == op.len); + + // Read it back - without the fix, verify_read_checksums returns -EDOM + // because the recomputed CRC includes zero-padding for [16K..20K) while + // the stored CRC was computed only over [20K..24K). + printf("reading\n"); + blockstore_op_t op2; + op2.opcode = BS_OP_READ; + op2.oid = { .inode = 1, .stripe = 0 }; + op2.version = UINT64_MAX; + op2.offset = 0; + op2.len = 128*1024; + op2.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, 128*1024); + test.exec_op(&op2); + assert(op2.retval == op2.len); + assert(is_zero(op2.buf, 20*1024)); + assert(memcheck(op2.buf+20*1024, 0xaa, 4*1024)); + assert(is_zero(op2.buf+24*1024, 104*1024)); + + free(op.buf); + free(op2.buf); +} + // FIXME Add a simple intent_write / big_intent test int main(int narg, char *args[]) @@ -844,6 +888,7 @@ int main(int narg, char *args[]) test_intent_over_unstable(); test_padded_csum_intent(false); test_padded_csum_intent(true); + test_padded_csum_sparse_leading_hole(); test_perfect_csum_interrupted(); test_padded_csum_parallel_read(false, 8192); test_padded_csum_parallel_read(true, 8192); diff --git a/src/test/test_heap.cpp b/src/test/test_heap.cpp index 4f09028f..b23e45e6 100644 --- a/src/test/test_heap.cpp +++ b/src/test/test_heap.cpp @@ -2719,6 +2719,52 @@ void test_postpone_load() assert(obj); assert(count_writes(heap, obj) == 11); } + + printf("OK test_postpone_load\n"); +} + +void test_big_padded_csum() +{ + blockstore_disk_t dsk; + _test_init(dsk, true, [](std::map & config) + { + config["csum_block_size"] = "32k"; + }); + std::vector buffer_area(dsk.journal_device_size); + blockstore_heap_t heap(&dsk, buffer_area.data()); + heap.finish_recheck(); + + { + memset(buffer_area.data()+4096, 0xab, 4*1024); + _test_big_write(heap, dsk, 1, 0, 1, 0, true, 36*1024, 4*1024, buffer_area.data()+4096); + + object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 }; + heap_entry_t *obj = heap.lock_and_read_entry(oid); + assert(obj); + + // Read the whole checksum block and verify its checksum + bool csum_ok = heap.calc_block_checksums( + (uint32_t*)obj->get_checksums(&heap) + 1, buffer_area.data(), obj->get_int_bitmap(&heap), + 32*1024, 32*1024+32*1024, false, [](uint32_t, uint32_t, uint32_t) {} + ); + assert(csum_ok); + + // Part 2 - check internal padding + memset(buffer_area.data()+12*1024, 0xab, 4*1024); + std::vector bitmap(dsk.clean_entry_bitmap_size); + bitmap_set(bitmap.data(), 36*1024, 4*1024, 4096); + bitmap_set(bitmap.data(), 44*1024, 4*1024, 4096); + std::vector new_csums(dsk.data_block_size/32768*4); + memcpy(new_csums.data(), obj->get_checksums(&heap), new_csums.size()); + ((uint32_t*)new_csums.data())[1] = crc32c(0, buffer_area.data()+4*1024, 12*1024); + csum_ok = heap.calc_block_checksums( + (uint32_t*)new_csums.data() + 1, buffer_area.data(), bitmap.data(), + 32*1024, 32*1024+32*1024, false, [](uint32_t, uint32_t, uint32_t) {} + ); + assert(csum_ok); + } + + printf("OK test_big_padded_csum\n"); } // FIXME: Add a test for big_intent, incl. explicit_complete with big_intent over big_write over deletion over big_write :) @@ -2759,5 +2805,6 @@ int main(int narg, char *args[]) test_explicit_complete(); test_skip_double_claim(); test_postpone_load(); + test_big_padded_csum(); return 0; }