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