diff --git a/lfs3.c b/lfs3.c index f189ee7b..62570f51 100644 --- a/lfs3.c +++ b/lfs3.c @@ -7786,10 +7786,18 @@ static void lfs3_fs_commitgdelta(lfs3_t *lfs3) { // keep track of the on-disk grm lfs3_data_fromgrm(&lfs3->grm, lfs3->grm_p); - // keep track of the on-disk gbmap #ifdef LFS3_GBMAP + // keep track of the on-disk gbmap if (lfs3_f_isgbmap(lfs3->flags)) { + // keep track of both the committed gstate and btree for + // traversals + lfs3->gbmap.b_p = lfs3->gbmap.b; lfs3_data_fromgbmap(&lfs3->gbmap, lfs3->gbmap_p); + + // if disabled, we still want to keep track of the on-disk gstate + // in case the user wants to re-enable the gbmap + } else { + lfs3_memxor(lfs3->gbmap_p, lfs3->gbmap_d, LFS3_GBMAP_DSIZE); } #endif } @@ -10387,25 +10395,12 @@ static lfs3_stag_t lfs3_mtree_traverse_(lfs3_t *lfs3, lfs3_trv_t *trv, == LFS3_TSTATE_GBMAP, false)) { #ifdef LFS3_GBMAP - // decode the on-disk gbmap - // - // TODO this adds 64 bytes of mostly unused stack - // to the stack hot-path, can we avoid this somehow? - // do we care in gbmap mode? - // - lfs3_gbmap_t gbmap_p; - err = lfs3_data_readgbmap(lfs3, - &LFS3_DATA_BUF(lfs3->gbmap_p, - LFS3_GBMAP_DSIZE), - &gbmap_p); - if (err) { - LFS3_UNREACHABLE(); - } - // if on-disk gbmap does not match the active gbmap, // transition to traversing the on-disk gbmap - if (lfs3_btree_cmp(&gbmap_p.b, &lfs3->gbmap.b) != 0) { - trv->b.shrub = gbmap_p.b; + if (lfs3_btree_cmp( + &lfs3->gbmap.b_p, + &lfs3->gbmap.b) != 0) { + trv->b.shrub = lfs3->gbmap.b_p; trv->bid = -2; lfs3_t_settstate(&trv->b.h.flags, LFS3_TSTATE_GBMAP_P); @@ -10723,6 +10718,15 @@ eot:; /// Optional on-disk block map /// +#if !defined(LFS3_RDONLY) && !defined(LFS3_2BONLY) && defined(LFS3_GBMAP) +static void lfs3_gbmap_init(lfs3_gbmap_t *gbmap) { + gbmap->window = 0; + gbmap->known = 0; + lfs3_btree_init(&gbmap->b); + lfs3_btree_init(&gbmap->b_p); +} +#endif + #if !defined(LFS3_RDONLY) && !defined(LFS3_2BONLY) && defined(LFS3_GBMAP) static lfs3_data_t lfs3_data_fromgbmap(const lfs3_gbmap_t *gbmap, uint8_t buffer[static LFS3_GBMAP_DSIZE]) { @@ -10775,6 +10779,8 @@ static int lfs3_data_readgbmap(lfs3_t *lfs3, lfs3_data_t *data, // make sure to zero btree leaf lfs3_btree_discardleaf(&gbmap->b); + // and keep track of the committed gbmap for traversals + gbmap->b_p = gbmap->b; return 0; } #endif @@ -11310,8 +11316,6 @@ static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) { #if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) static int lfs3_alloc_rebuildgbmap(lfs3_t *lfs3) { - // we should ckpoint before calling this - LFS3_ASSERT(lfs3->lookahead.ckpoint == lfs3->block_count); LFS3_INFO("Rebuilding gbmap " "(gbmap %"PRId32"/%"PRId32")", lfs3->lookahead.gbmapped, @@ -15544,6 +15548,7 @@ static int lfs3_init(lfs3_t *lfs3, uint32_t flags, // TODO are these zeros accomplished by flushgdelta in mountinited? // should the flushgdelta be dropped? + // TODO should we just call flushgdelta here? // zero gstate lfs3->gcksum = 0; @@ -15560,11 +15565,10 @@ static int lfs3_init(lfs3_t *lfs3, uint32_t flags, #endif // setup other global gbmap state - // TODO is this actually needed? #ifdef LFS3_GBMAP - lfs3_btree_init(&lfs3->gbmap.b); - lfs3->gbmap.window = 0; - lfs3->gbmap.known = 0; + lfs3_gbmap_init(&lfs3->gbmap); + // TODO should this be in the gbmap struct? + lfs3->lookahead.gbmapped = 0; lfs3_memset(lfs3->gbmap_p, 0, LFS3_GBMAP_DSIZE); lfs3_memset(lfs3->gbmap_d, 0, LFS3_GBMAP_DSIZE); #endif @@ -17112,6 +17116,105 @@ failed:; } #endif +// enable the global on-disk block-map +#if !defined(LFs3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP) +int lfs3_fs_mkgbmap(lfs3_t *lfs3) { + // do nothing if we already have a gbmap + if (lfs3_f_isgbmap(lfs3->flags)) { + return 0; + } + + // prepare our filesystem for writing + int err = lfs3_fs_mkconsistent(lfs3); + if (err) { + return err; + } + + // checkpoint the allocator + // TODO, should lfs3_fs_mkconsistent also checkpoint the allocator? + err = lfs3_alloc_ckpoint(lfs3); + if (err) { + return err; + } + + // create an empty gbmap + lfs3_gbmap_init(&lfs3->gbmap); + // TODO should this be in the gbmap struct? + lfs3->lookahead.gbmapped = 0; + + // start with everything free, rebuilding the gbmap will populate it + err = lfs3_gbmap_commit(lfs3, &lfs3->gbmap.b, 0, LFS3_RATTRS( + LFS3_RATTR(LFS3_TAG_BMFREE, +lfs3->block_count))); + if (err) { + goto failed; + } + + // go ahead and mark gbmap as in-use internally + lfs3->flags |= LFS3_F_GBMAP; + + // checkpoint the allocator, this should trigger a rebuild + err = lfs3_alloc_ckpoint(lfs3); + if (err) { + goto failed; + } + + // mark the gbmap as in-use on-disk while atomically committing the + // gbmap into gstate + lfs3_wcompat_t wcompat_ = lfs3_wcompat(lfs3); + wcompat_ |= LFS3_WCOMPAT_GBMAP; + + err = lfs3_mdir_commit(lfs3, &lfs3->mroot, LFS3_RATTRS( + LFS3_RATTR_LE32(LFS3_TAG_WCOMPAT, 0, wcompat_))); + if (err) { + goto failed; + } + + return 0; + +failed:; + // if we failed clear the gbmap bit and reset the gbmap to be safe + lfs3->flags &= ~LFS3_F_GBMAP; + lfs3_gbmap_init(&lfs3->gbmap); + // TODO should this be in the gbmap struct? + lfs3->lookahead.gbmapped = 0; + return err; +} +#endif + +// disable the global on-disk block-map +#if !defined(LFs3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP) +int lfs3_fs_rmgbmap(lfs3_t *lfs3) { + // do nothing if we already don't have a gbmap + if (!lfs3_f_isgbmap(lfs3->flags)) { + return 0; + } + + // prepare our filesystem for writing + int err = lfs3_fs_mkconsistent(lfs3); + if (err) { + return err; + } + + // removing the gbmap is relatively easy, we just need to mark the + // gbmap as not in use + // + // this leaves garbage gdeltas around, but these should be cleaned + // up implicitly as mdirs are compacted + lfs3_wcompat_t wcompat_ = lfs3_wcompat(lfs3); + wcompat_ &= ~LFS3_WCOMPAT_GBMAP; + + err = lfs3_mdir_commit(lfs3, &lfs3->mroot, LFS3_RATTRS( + LFS3_RATTR_LE32(LFS3_TAG_WCOMPAT, 0, wcompat_))); + if (err) { + return err; + } + + // on success mark gbmap as not-in-use internally + lfs3->flags &= ~LFS3_F_GBMAP; + return 0; +} +#endif + /// High-level filesystem traversal /// diff --git a/lfs3.h b/lfs3.h index f2912840..f4fbec29 100644 --- a/lfs3.h +++ b/lfs3.h @@ -849,6 +849,7 @@ typedef struct lfs3_gbmap { lfs3_block_t window; lfs3_block_t known; lfs3_btree_t b; + lfs3_btree_t b_p; } lfs3_gbmap_t; @@ -1413,5 +1414,21 @@ int lfs3_fs_unck(lfs3_t *lfs3, uint32_t flags); int lfs3_fs_grow(lfs3_t *lfs3, lfs3_size_t block_count); #endif +// Enable the global on-disk block-map +// +// Returns a negative error code on failure. Does nothing if a gbmap +// already exists. +#if !defined(LFs3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP) +int lfs3_fs_mkgbmap(lfs3_t *lfs3); +#endif + +// Disable the global on-disk block-map +// +// Returns a negative error code on failure. Does nothing if no gbmap +// is found. +#if !defined(LFs3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP) +int lfs3_fs_rmgbmap(lfs3_t *lfs3); +#endif + #endif diff --git a/tests/test_alloc.toml b/tests/test_alloc.toml index 20ed2b55..15470369 100644 --- a/tests/test_alloc.toml +++ b/tests/test_alloc.toml @@ -20,7 +20,7 @@ defines.COUNT = [ 'BLOCK_COUNT/2', 'BLOCK_COUNT/4', '5', - '2', + 'FORMAT_BLOCK_COUNT', ] defines.ERASE = [false, true] if = 'COUNT >= FORMAT_BLOCK_COUNT' @@ -67,7 +67,7 @@ defines.COUNT = [ 'BLOCK_COUNT/2', 'BLOCK_COUNT/4', '5', - '2', + 'FORMAT_BLOCK_COUNT', ] defines.ERASE = [false, true] if = 'COUNT >= FORMAT_BLOCK_COUNT' @@ -643,10 +643,102 @@ code = ''' ''' -# TODO more nospc tests (opened files? other?) -# nospc tests mostly test that things still work when block allocation -# wraparound occurs +# test that alloc can wrap around the disk +[cases.test_alloc_wraparound_files] +defines.COUNT = [ + 'BLOCK_COUNT', + 'BLOCK_COUNT-1', + 'BLOCK_COUNT/2', + 'BLOCK_COUNT/4', + '5', + 'FORMAT_BLOCK_COUNT', +] +defines.SIZE = [ + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +defines.N = ['5', 'BLOCK_COUNT/2'] +defines.WRAPAROUND = 3 +if = [ + 'COUNT >= FORMAT_BLOCK_COUNT', + 'COUNT >= 2*N*SIZE/BLOCK_SIZE', +] +code = ''' + // test various block counts + struct lfs3_cfg cfg = *CFG; + cfg.block_count = COUNT; + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + + // create n files repeatedly until we're sure we've wrapped around + // a few times + uint32_t prng = 42; + uint32_t prng_ = prng; + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // check that our file writes worked + prng_ = prng; + for (lfs3_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "file%08d", i); + struct lfs3_info info; + lfs3_stat(&lfs3, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + uint8_t rbuf[SIZE]; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfs3_file_close(&lfs3, &file) => 0; + } + } + + lfs3_unmount(&lfs3) => 0; +''' + + + +# test that alloc works up until nospc [cases.test_alloc_nospc_dirs] defines.COUNT = [ 'BLOCK_COUNT', @@ -654,7 +746,7 @@ defines.COUNT = [ 'BLOCK_COUNT/2', 'BLOCK_COUNT/4', '5', - '2', + 'FORMAT_BLOCK_COUNT', ] if = 'COUNT >= FORMAT_BLOCK_COUNT' code = ''' @@ -728,7 +820,7 @@ defines.COUNT = [ 'BLOCK_COUNT/2', 'BLOCK_COUNT/4', '5', - '2', + 'FORMAT_BLOCK_COUNT', ] defines.SIZE = [ '0', diff --git a/tests/test_gbmap.toml b/tests/test_gbmap.toml index 58bb3032..cddb7cf4 100644 --- a/tests/test_gbmap.toml +++ b/tests/test_gbmap.toml @@ -9,7 +9,7 @@ after = ['test_btree', 'test_mtree'] ifdef = 'LFS3_GBMAP' -# test gbmap operations +# test low-level gbmap operations # test simple set operations [cases.test_gbmap_set_split] @@ -384,3 +384,786 @@ code = ''' free(sim); lfs3_unmount(&lfs3) => 0; ''' + + + +# test high-level gbmap operations + +# test that the gbmap generally works +[cases.test_gbmap_files] +defines.COUNT = [ + 'BLOCK_COUNT', + 'BLOCK_COUNT-1', + 'BLOCK_COUNT/2', + 'BLOCK_COUNT/4', + '5', + '3', +] +defines.SIZE = [ + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +defines.N = ['5', 'BLOCK_COUNT/2'] +defines.WRAPAROUND = 3 +if = [ + 'COUNT >= 3', + 'COUNT >= 2*N*SIZE/BLOCK_SIZE', +] +code = ''' + // test various block counts + struct lfs3_cfg cfg = *CFG; + cfg.block_count = COUNT; + lfs3_t lfs3; + // note the gbmap flag + lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + + // check that we were formatted with the gbmap + struct lfs3_fsinfo fsinfo; + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(fsinfo.flags & LFS3_I_GBMAP); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + uint32_t prng = 42; + uint32_t prng_ = prng; + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // check that our file writes worked + prng_ = prng; + for (lfs3_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "file%08d", i); + struct lfs3_info info; + lfs3_stat(&lfs3, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + uint8_t rbuf[SIZE]; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfs3_file_close(&lfs3, &file) => 0; + } + } + + lfs3_unmount(&lfs3) => 0; +''' + +# test that we can remove the gbmap and things still work +[cases.test_gbmap_rmgbmap] +ifndef = 'LFS3_YES_GBMAP' +defines.COUNT = [ + 'BLOCK_COUNT', + 'BLOCK_COUNT-1', + 'BLOCK_COUNT/2', + 'BLOCK_COUNT/4', + '5', + '3', +] +defines.SIZE = [ + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +defines.N = ['5', 'BLOCK_COUNT/2'] +defines.WRAPAROUND = 3 +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after +# REMOUNT=2 => remount before +# REMOUNT=3 => remount both after and before +defines.REMOUNT = [0, 1, 2, 3] +if = [ + 'COUNT >= 3', + 'COUNT >= 2*N*SIZE/BLOCK_SIZE', +] +code = ''' + // test various block counts + struct lfs3_cfg cfg = *CFG; + cfg.block_count = COUNT; + lfs3_t lfs3; + // note the gbmap flag + lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + + // check that we were formatted with the gbmap + struct lfs3_fsinfo fsinfo; + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(fsinfo.flags & LFS3_I_GBMAP); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + uint32_t prng = 42; + uint32_t prng_ = prng; + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + // remount before? + if (REMOUNT & 2) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // now remove the gbmap + lfs3_fs_rmgbmap(&lfs3) => 0; + + // remount after? + if (REMOUNT & 1) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // we should no longer have a gbmap + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS3_I_GBMAP)); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // check that our file writes worked + prng_ = prng; + for (lfs3_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "file%08d", i); + struct lfs3_info info; + lfs3_stat(&lfs3, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + uint8_t rbuf[SIZE]; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfs3_file_close(&lfs3, &file) => 0; + } + } + + lfs3_unmount(&lfs3) => 0; +''' + +# test that we can make the gbmap and things still work +[cases.test_gbmap_mkgbmap] +ifndef = 'LFS3_YES_GBMAP' +defines.COUNT = [ + 'BLOCK_COUNT', + 'BLOCK_COUNT-1', + 'BLOCK_COUNT/2', + 'BLOCK_COUNT/4', + '5', + '3', +] +defines.SIZE = [ + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +defines.N = ['5', 'BLOCK_COUNT/2'] +defines.WRAPAROUND = 3 +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after +# REMOUNT=2 => remount before +# REMOUNT=3 => remount both after and before +defines.REMOUNT = [0, 1, 2, 3] +if = [ + 'COUNT >= 3', + 'COUNT >= 2*N*SIZE/BLOCK_SIZE', +] +code = ''' + // test various block counts + struct lfs3_cfg cfg = *CFG; + cfg.block_count = COUNT; + lfs3_t lfs3; + // note the lack of gbmap flag + lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + + // check that we were formatted without the gbmap + struct lfs3_fsinfo fsinfo; + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS3_I_GBMAP)); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + uint32_t prng = 42; + uint32_t prng_ = prng; + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + // remount before? + if (REMOUNT & 2) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // now make the gbmap + lfs3_fs_mkgbmap(&lfs3) => 0; + + // remount after? + if (REMOUNT & 1) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // we should now have a gbmap + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(fsinfo.flags & LFS3_I_GBMAP); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // check that our file writes worked + prng_ = prng; + for (lfs3_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "file%08d", i); + struct lfs3_info info; + lfs3_stat(&lfs3, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + uint8_t rbuf[SIZE]; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfs3_file_close(&lfs3, &file) => 0; + } + } + + lfs3_unmount(&lfs3) => 0; +''' + +# test that we can remove and then remake the gbmap and things still +# work +# +# this leaves the disk with garbage gstate that mkgbmap needs to clean +# up +[cases.test_gbmap_rmmkgbmap] +ifndef = 'LFS3_YES_GBMAP' +defines.COUNT = [ + 'BLOCK_COUNT', + 'BLOCK_COUNT-1', + 'BLOCK_COUNT/2', + 'BLOCK_COUNT/4', + '5', + '3', +] +defines.SIZE = [ + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +defines.N = ['5', 'BLOCK_COUNT/2'] +defines.WRAPAROUND = 3 +# REMOUNT=0x1 => remount after +# REMOUNT=0x2 => remount before make +# REMOUNT=0x4 => remount after remove +# REMOUNT=0x8 => remount before +defines.REMOUNT = 'range(0x10)' +if = [ + 'COUNT >= 3', + 'COUNT >= 2*N*SIZE/BLOCK_SIZE', +] +code = ''' + // test various block counts + struct lfs3_cfg cfg = *CFG; + cfg.block_count = COUNT; + lfs3_t lfs3; + // note the gbmap flag + lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + + // check that we were formatted with the gbmap + struct lfs3_fsinfo fsinfo; + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(fsinfo.flags & LFS3_I_GBMAP); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + uint32_t prng = 42; + uint32_t prng_ = prng; + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + // remount before? + if (REMOUNT & 8) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // now remove the gbmap + lfs3_fs_rmgbmap(&lfs3) => 0; + + // remount after remove? + if (REMOUNT & 4) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // we should no longer have a gbmap + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS3_I_GBMAP)); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + // remount before make? + if (REMOUNT & 2) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // now remake the gbmap + lfs3_fs_mkgbmap(&lfs3) => 0; + + // remount after? + if (REMOUNT & 1) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // we should now have a gbmap + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(fsinfo.flags & LFS3_I_GBMAP); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // check that our file writes worked + prng_ = prng; + for (lfs3_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "file%08d", i); + struct lfs3_info info; + lfs3_stat(&lfs3, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + uint8_t rbuf[SIZE]; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfs3_file_close(&lfs3, &file) => 0; + } + } + + lfs3_unmount(&lfs3) => 0; +''' + +# test that we can make and then remove the gbmap and things still +# work +# +# not exactly sure what this tests, but including for completeness +[cases.test_gbmap_mkrmgbmap] +ifndef = 'LFS3_YES_GBMAP' +defines.COUNT = [ + 'BLOCK_COUNT', + 'BLOCK_COUNT-1', + 'BLOCK_COUNT/2', + 'BLOCK_COUNT/4', + '5', + '3', +] +defines.SIZE = [ + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +defines.N = ['5', 'BLOCK_COUNT/2'] +defines.WRAPAROUND = 3 +# REMOUNT=0x1 => remount after +# REMOUNT=0x2 => remount before remove +# REMOUNT=0x4 => remount after make +# REMOUNT=0x8 => remount before +defines.REMOUNT = 'range(0x10)' +if = [ + 'COUNT >= 3', + 'COUNT >= 2*N*SIZE/BLOCK_SIZE', +] +code = ''' + // test various block counts + struct lfs3_cfg cfg = *CFG; + cfg.block_count = COUNT; + lfs3_t lfs3; + // note the lack of gbmap flag + lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + + // check that we were formatted without the gbmap + struct lfs3_fsinfo fsinfo; + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS3_I_GBMAP)); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + uint32_t prng = 42; + uint32_t prng_ = prng; + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + // remount before? + if (REMOUNT & 8) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // now make the gbmap + lfs3_fs_mkgbmap(&lfs3) => 0; + + // remount after make? + if (REMOUNT & 4) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // we should now have a gbmap + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(fsinfo.flags & LFS3_I_GBMAP); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + // remount before remove? + if (REMOUNT & 2) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // now remove the gbmap + lfs3_fs_rmgbmap(&lfs3) => 0; + + // remount after? + if (REMOUNT & 1) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // we should no longer have a gbmap + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS3_I_GBMAP)); + + // create n files repeatedly until we're sure we've wrapped around + // a few times + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // check that our file writes worked + prng_ = prng; + for (lfs3_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "file%08d", i); + struct lfs3_info info; + lfs3_stat(&lfs3, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + uint8_t rbuf[SIZE]; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfs3_file_close(&lfs3, &file) => 0; + } + } + + lfs3_unmount(&lfs3) => 0; +'''