From 109bd4e0ab708b4f1ba46750ffd9bff8350c0bfd Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 19 Jan 2025 12:35:48 -0600 Subject: [PATCH] Added lfsr_fs_cksum This just exposes the gcksum to the user, but exposing the gcksum allows the user to store it externally for an extra layer of protection against filesystem corruption. As far as I'm aware this is the only real way to protect against global rollback issues, which is a problem for any filesystem with logs (aka any powerloss-resilient filesystem). This required a comically small amount of code: code stack ctx before: 38492 2624 640 after: 38500 (+0.0%) 2624 (+0.0%) 640 (+0.0%) --- lfs.c | 6 ++++ lfs.h | 16 +++++++++++ tests/test_ck.toml | 71 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 86 insertions(+), 7 deletions(-) diff --git a/lfs.c b/lfs.c index c86246c4..8e23038e 100644 --- a/lfs.c +++ b/lfs.c @@ -14519,6 +14519,12 @@ int lfsr_fs_ckdata(lfs_t *lfs) { return lfsr_fs_ck(lfs, LFS_T_CKMETA | LFS_T_CKDATA); } +// get the filesystem checksum +int lfsr_fs_cksum(lfs_t *lfs, uint32_t *cksum) { + *cksum = lfs->gcksum; + return 0; +} + // low-level filesystem gc // // runs the traversal until all work is completed, which may take diff --git a/lfs.h b/lfs.h index c6fa5870..dbc8c704 100644 --- a/lfs.h +++ b/lfs.h @@ -1306,6 +1306,22 @@ int lfsr_fs_ckmeta(lfs_t *lfs); int lfsr_fs_ckdata(lfs_t *lfs); #endif +// Get the current filesystem checksum +// +// This is a checksum of all metadata + data in the filesystem, which +// can be stored externally to provide increased protection against +// filesystem corruption. +// +// Note this checksum is order-sensitive. So while it's unlikely two +// filesystems with different contents will have the same checksum, two +// filesystems with the same contents may not have the same checksum. +// +// Also note this is only a 32-bit checksum. Collisions should be +// expected. +// +// Returns a negative error code on failure. +int lfsr_fs_cksum(lfs_t *lfs, uint32_t *cksum); + #ifdef LFS_GC // Perform any janitorial work that may be pending // diff --git a/tests/test_ck.toml b/tests/test_ck.toml index 7559cec2..777d9337 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -123,6 +123,61 @@ code = ''' # Test filesystem-level checksum things +# test that lfsr_fs_cksum doesn't do anything weird +[cases.test_ck_cksum] +defines.N = [1, 2, 4, 8, 16, 32, 64] +defines.SIZE = [ + '0', + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +if = [ + '(SIZE*N)/BLOCK_SIZE <= 32', + 'LFS_IFDEF_GC(true, METHOD != 1)', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an interesting filesystem + uint32_t prng = 42; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + sprintf(name, "squid%03x", i); + + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + } + + // get the filesystem cksum + uint32_t gcksum; + lfsr_fs_cksum(&lfs, &gcksum) => 0; + printf("cksum: %08x\n", gcksum); + + // test that the cksum remains the same after a remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + uint32_t gcksum_; + lfsr_fs_cksum(&lfs, &gcksum_) => 0; + assert(gcksum_ == gcksum); + + lfsr_unmount(&lfs) => 0; +''' + # test we can detect at least fully clobbered blocks [cases.test_ck_ckmeta_easy] # METHOD=0 => lfsr_fs_ckmeta @@ -483,9 +538,9 @@ code = ''' } clobber:; - // TODO API for this? // save the current gcksum - uint32_t gcksum = lfs.gcksum; + uint32_t gcksum; + lfsr_fs_cksum(&lfs, &gcksum) => 0; // try flipping some bits for (lfs_size_t j = 0; j < M; j++) { @@ -604,8 +659,9 @@ code = ''' // end up different, this allows detecting rollback if // stored externally if (found != N) { - // TODO API for this? - assert(lfs.gcksum != gcksum); + uint32_t gcksum_; + lfsr_fs_cksum(&lfs, &gcksum_) => 0; + assert(gcksum_ != gcksum); } // test we can read the files that survived @@ -737,9 +793,9 @@ code = ''' } clobber:; - // TODO API for this? // save the current gcksum uint32_t gcksum = lfs.gcksum; + lfsr_fs_cksum(&lfs, &gcksum) => 0; // try flipping some bits for (lfs_size_t j = 0; j < M; j++) { @@ -858,8 +914,9 @@ code = ''' // end up different, this allows detecting rollback if // stored externally if (found != N) { - // TODO API for this? - assert(lfs.gcksum != gcksum); + uint32_t gcksum_; + lfsr_fs_cksum(&lfs, &gcksum_) => 0; + assert(gcksum_ != gcksum); } // test we can read the files that survived