# Test incremental traversal things after = [ 'test_dirs', 'test_files', 'test_fwrite', 'test_stickynotes', 'test_alloc' ] # Test both with and without the gbmap if available defines.GBMAP = [false, true] if = ''' LFS3_IFDEF_YES_GBMAP( GBMAP, LFS3_IFDEF_GBMAP(true, !GBMAP)) ''' # a simple traversal test [cases.test_trvs_simple] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = 'GBMAP || !REPOPGBMAP' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; ''' # can we rewind? [cases.test_trvs_rewind] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_rewind(&lfs3, &trv) => 0; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that we don't get extra anything after end of traversal [cases.test_trvs_idempotent] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = 'GBMAP || !REPOPGBMAP' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; ''' # some simple traversal tests with clobbering [cases.test_trvs_clobber_dirs] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many directories for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_mkdir(&lfs3, name) => 0; } // traverse to find all blocks in use uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } printf("traversal: btype %d block 0x%x\n", tinfo.btype, tinfo.block); assert(tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE); // keep track of seen blocks seen[tinfo.block / 8] |= 1 << (tinfo.block % 8); } lfs3_trv_close(&lfs3, &trv) => 0; // clobber every other block uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) { if (!(seen[block / 8] & (1 << (block % 8)))) { CFG->erase(CFG, block) => 0; CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0; } } free(seen); // then check that we can read our directories after clobbering for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_clobber_files] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); 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_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // traverse to find all blocks in use uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } printf("traversal: btype %d block 0x%x\n", tinfo.btype, tinfo.block); assert(tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE || tinfo.btype == LFS3_BTYPE_DATA); // keep track of seen blocks seen[tinfo.block / 8] |= 1 << (tinfo.block % 8); } lfs3_trv_close(&lfs3, &trv) => 0; // clobber every other block uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) { if (!(seen[block / 8] & (1 << (block % 8)))) { CFG->erase(CFG, block) => 0; CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0; } } free(seen); // then check that reading our files still works after clobbering for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } prng = 42; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "file%03x", 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; ''' [cases.test_trvs_clobber_files_opened] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files lfs3_file_t files[N]; uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_open(&lfs3, &files[i], name, LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } // traverse to find all blocks in use uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } printf("traversal: btype %d block 0x%x\n", tinfo.btype, tinfo.block); assert(tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE || tinfo.btype == LFS3_BTYPE_DATA); // keep track of seen blocks seen[tinfo.block / 8] |= 1 << (tinfo.block % 8); } lfs3_trv_close(&lfs3, &trv) => 0; // clobber every other block uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) { if (!(seen[block / 8] & (1 << (block % 8)))) { CFG->erase(CFG, block) => 0; CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0; } } free(seen); // then check that reading our files still works after clobbering prng = 42; for (lfs3_size_t i = 0; i < N; i++) { // 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); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // and everything is fine after saving the files for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } prng = 42; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "file%03x", 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; ''' # a bit more aggressive rewind tests [cases.test_trvs_rewind_clobber_dirs] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many directories for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_mkdir(&lfs3, name) => 0; } // traverse to find all blocks in use uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; lfs3_block_t r = 0; for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); if (i == r) { lfs3_trv_rewind(&lfs3, &trv) => 0; memset(seen, 0, (BLOCK_COUNT+7)/8); r += 1; i = -1; continue; } struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } printf("traversal: btype %d block 0x%x\n", tinfo.btype, tinfo.block); assert(tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE); // keep track of seen blocks seen[tinfo.block / 8] |= 1 << (tinfo.block % 8); } lfs3_trv_close(&lfs3, &trv) => 0; // clobber every other block uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) { if (!(seen[block / 8] & (1 << (block % 8)))) { CFG->erase(CFG, block) => 0; CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0; } } free(seen); // then check that we can read our directories after clobbering for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_rewind_clobber_files] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); 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_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // traverse to find all blocks in use uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; lfs3_block_t r = 0; for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); if (i == r) { lfs3_trv_rewind(&lfs3, &trv) => 0; memset(seen, 0, (BLOCK_COUNT+7)/8); r += 1; i = -1; continue; } struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } printf("traversal: btype %d block 0x%x\n", tinfo.btype, tinfo.block); assert(tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE || tinfo.btype == LFS3_BTYPE_DATA); // keep track of seen blocks seen[tinfo.block / 8] |= 1 << (tinfo.block % 8); } lfs3_trv_close(&lfs3, &trv) => 0; // clobber every other block uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) { if (!(seen[block / 8] & (1 << (block % 8)))) { CFG->erase(CFG, block) => 0; CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0; } } free(seen); // then check that reading our files still works after clobbering for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } prng = 42; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "file%03x", 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; ''' [cases.test_trvs_rewind_clobber_files_opened] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files lfs3_file_t files[N]; uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_open(&lfs3, &files[i], name, LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } // traverse to find all blocks in use uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; lfs3_block_t r = 0; for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); if (i == r) { lfs3_trv_rewind(&lfs3, &trv) => 0; memset(seen, 0, (BLOCK_COUNT+7)/8); r += 1; i = -1; continue; } struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } printf("traversal: btype %d block 0x%x\n", tinfo.btype, tinfo.block); assert(tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE || tinfo.btype == LFS3_BTYPE_DATA); // keep track of seen blocks seen[tinfo.block / 8] |= 1 << (tinfo.block % 8); } lfs3_trv_close(&lfs3, &trv) => 0; // clobber every other block uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) { if (!(seen[block / 8] & (1 << (block % 8)))) { CFG->erase(CFG, block) => 0; CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0; } } free(seen); // then check that reading our files still works after clobbering prng = 42; for (lfs3_size_t i = 0; i < N; i++) { // 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); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // and everything is fine after saving the files for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } prng = 42; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "file%03x", 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; ''' # check that we can detect every clobbered mdir [cases.test_trvs_ckmdir_dirs] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] code = ''' for (lfs3_block_t i = 0;; i += 2) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many directories for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_mkdir(&lfs3, name) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_MDIR) { if (k == i || k == i+1) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; if (k == i+1) { lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; } done:; ''' [cases.test_trvs_ckmdir_files] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' for (lfs3_block_t i = 0;; i += 2) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); 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_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_MDIR) { if (k == i || k == i+1) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; if (k == i+1) { lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; } done:; ''' [cases.test_trvs_ckmdir_files_opened] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' for (lfs3_block_t i = 0;; i += 2) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files lfs3_file_t files[N]; uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_open(&lfs3, &files[i], name, LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_MDIR) { if (k == i || k == i+1) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; if (k == i+1) { lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_desync(&lfs3, &files[i]) => 0; lfs3_file_close(&lfs3, &files[i]) => 0; } lfs3_unmount(&lfs3) => 0; } done:; ''' # check that we can detect every clobbered btree [cases.test_trvs_ckbtree_dirs] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] code = ''' for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many directories for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_mkdir(&lfs3, name) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_BTREE) { if (k == i) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; } done:; ''' [cases.test_trvs_ckbtree_files] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); 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_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_BTREE) { if (k == i) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; } done:; ''' [cases.test_trvs_ckbtree_files_opened] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files lfs3_file_t files[N]; uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_open(&lfs3, &files[i], name, LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_BTREE) { if (k == i) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_desync(&lfs3, &files[i]) => 0; lfs3_file_close(&lfs3, &files[i]) => 0; } lfs3_unmount(&lfs3) => 0; } done:; ''' # check that we can detect every clobbered data block [cases.test_trvs_ckdata_dirs] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] code = ''' for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many directories for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_mkdir(&lfs3, name) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_DATA) { if (k == i) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; } done:; ''' [cases.test_trvs_ckdata_files] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); 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_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_DATA) { if (k == i) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; } done:; ''' [cases.test_trvs_ckdata_files_opened] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' for (lfs3_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create this many files lfs3_file_t files[N]; uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "file%03x", i); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_open(&lfs3, &files[i], name, LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } lfs3_unmount(&lfs3) => 0; goto done; } if (tinfo.btype == LFS3_BTYPE_DATA) { if (k == i) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; lfs3_trv_close(&lfs3, &trv) => 0; goto clobbered; } k += 1; } } clobbered:; // traverse again, we should detect the clobbered metadata lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_CORRUPT); // found the clobbered metadata? if (err == LFS3_ERR_CORRUPT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_desync(&lfs3, &files[i]) => 0; lfs3_file_close(&lfs3, &files[i]) => 0; } lfs3_unmount(&lfs3) => 0; } done:; ''' # test that in general fsinfo flags work [cases.test_trvs_flags] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = 'GBMAP || !REPOPGBMAP' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // check flags before struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_MKCONSISTENT | LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // check flags after lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!MKCONSISTENT) ? LFS3_I_MKCONSISTENT : 0) | ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) | ((!COMPACT) ? LFS3_I_COMPACT : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' # test that we detect filesystem mutation during traversal [cases.test_trvs_mutation] defines.WHEN = [0, 1, 2] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; if (WHEN == 0) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (WHEN == 1) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (WHEN == 2) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' # test that we don't get extra anything after end of traversal [cases.test_trvs_mutation_idempotent] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // mutate lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try another mutation just for good measure lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // we should _not_ update lookahead/compact/ckmeta/ckdata lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mkdir] defines.WHEN = [0, 1, 2] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; if (WHEN == 0) { lfs3_mkdir(&lfs3, "spider") => 0; } struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (WHEN == 1) { lfs3_mkdir(&lfs3, "spider") => 0; } lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (WHEN == 2) { lfs3_mkdir(&lfs3, "spider") => 0; } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_rm] defines.WHEN = [0, 1, 2] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // make a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; if (WHEN == 0) { lfs3_remove(&lfs3, "spider") => 0; } struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (WHEN == 1) { lfs3_remove(&lfs3, "spider") => 0; } lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (WHEN == 2) { lfs3_remove(&lfs3, "spider") => 0; } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mv] defines.WHEN = [0, 1, 2] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // make a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; if (WHEN == 0) { lfs3_rename(&lfs3, "spider", "scorpion") => 0; } struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (WHEN == 1) { lfs3_rename(&lfs3, "spider", "scorpion") => 0; } lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } if (WHEN == 2) { lfs3_rename(&lfs3, "spider", "scorpion") => 0; } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' # some more complex mutation tests [cases.test_trvs_mutation_fwrite] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; while (true) { // rewrite the file every step of the traversal lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_TRUNC) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // step traversal struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP && SIZE >= BLOCK_SIZE/4) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_fwrite_opened] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] defines.SYNC = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file) => 0; if (SYNC) { lfs3_file_sync(&lfs3, &file) => 0; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; while (true) { // rewrite the file every step of the traversal lfs3_file_rewind(&lfs3, &file) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; if (SYNC) { lfs3_file_sync(&lfs3, &file) => 0; } // step traversal struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( // ok we might've updated lookahead if write stayed in cache ((!LOOKAHEAD || SIZE > FILE_CACHE_SIZE || SYNC) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP && SIZE >= BLOCK_SIZE/4) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_rewind(&lfs3, &file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; // and after close? lfs3_file_open(&lfs3, &file, "spider", 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 specific cases where we need to clobber traversals # # these assume quite a bit more and may be a bit fragile... # [cases.test_trvs_mutation_file_bsprout] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' defines.TRUNC = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // rewrite the file lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_file_btree] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' defines.INLINE_SIZE = 0 defines.TRUNC = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite the file lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_file_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' defines.TRUNC = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite the file lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_uncreat_bsprout] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // rewrite the file lfs3_file_rewind(&lfs3, &file1) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; // check the file contents lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_uncreat_btree] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' defines.INLINE_SIZE = 0 code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite the file lfs3_file_rewind(&lfs3, &file1) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; // check the file contents lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_uncreat_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite the file lfs3_file_rewind(&lfs3, &file1) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; // check the file contents lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_close_bsprout] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' defines.DESYNC = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // close the file if (DESYNC) { lfs3_file_desync(&lfs3, &file1) => 0; } lfs3_file_close(&lfs3, &file1) => 0; // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/etc, unless we're desynced struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((DESYNC) ? LFS3_I_MKCONSISTENT : 0) | ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC)) ? LFS3_I_CKMETA : 0) | ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_file_close(&lfs3, &file2) => 0; // check the file contents lfs3_file_t file; if (DESYNC) { lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT; } else { lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_close_btree] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' defines.INLINE_SIZE = 0 defines.DESYNC = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // close the file if (DESYNC) { lfs3_file_desync(&lfs3, &file1) => 0; } lfs3_file_close(&lfs3, &file1) => 0; // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/etc, unless we're desynced struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((DESYNC) ? LFS3_I_MKCONSISTENT : 0) | ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC)) ? LFS3_I_CKMETA : 0) | ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_file_close(&lfs3, &file2) => 0; // check the file contents lfs3_file_t file; if (DESYNC) { lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT; } else { lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_close_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' defines.DESYNC = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // close the file if (DESYNC) { lfs3_file_desync(&lfs3, &file1) => 0; } lfs3_file_close(&lfs3, &file1) => 0; // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/etc, unless we're desynced struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((DESYNC) ? LFS3_I_MKCONSISTENT : 0) | ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC)) ? LFS3_I_CKMETA : 0) | ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_file_close(&lfs3, &file2) => 0; // check the file contents lfs3_file_t file; if (DESYNC) { lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT; } else { lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_rm_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // remove the file lfs3_remove(&lfs3, "spider") => 0; // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/etc, unless we're desynced struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((DESYNC) ? LFS3_I_MKCONSISTENT : 0) | ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC)) ? LFS3_I_CKMETA : 0) | ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mv_src_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create three files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rename one file over another lfs3_rename(&lfs3, "spider", "tarantula") => 0; // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mv_dst_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rename one file over another lfs3_rename(&lfs3, "tarantula", "spider") => 0; // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mroot_split] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // create enough files for mroot to split lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "uloborus%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mroot_split_bshrub_l] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create three files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse a data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // create enough files for mroot to split lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "uloborus%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mroot_split_bshrub_r] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create three files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "zodarion", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // and another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // create enough files for mroot to split lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "uloborus%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "zodarion", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mroot_extend] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' # force early relocations defines.BLOCK_RECYCLES = 0 code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "uloborus") => 0; } // traverse mroot lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mroot_extend_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' # force early relocations defines.BLOCK_RECYCLES = 0 code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse a data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "uloborus") => 0; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mroot_relocate] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' # force early relocations defines.BLOCK_RECYCLES = 0 code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "uloborus") => 0; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // rewrite enough files for mroot to relocate lfs3_block_t orig = lfs3.mroot.r.blocks[0]; while (lfs3.mroot.r.blocks[0] == orig || lfs3.mroot.r.blocks[0] == orig) { lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "uloborus") => 0; } // it's a bit unclear if clobbered mroot chain traversals should // still traverse inlined mroots, so if this breaks in the future // I wouldn't worry too much about it // // traverse mroot lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mroot_relocate_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' # force early relocations defines.BLOCK_RECYCLES = 0 code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "uloborus") => 0; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroots struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse a data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite enough files for mroot to relocate lfs3_block_t orig = lfs3.mroot.r.blocks[0]; while (lfs3.mroot.r.blocks[0] == orig || lfs3.mroot.r.blocks[0] == orig) { lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "uloborus") => 0; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mtree_split] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "tarantula%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "xnotata%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // create enough files for mdir to split again i = 0; orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "vulsor%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mtree_split_bshrub_l] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "tarantula%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "xnotata%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // create enough files for mdir to split again i = 0; orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "vulsor%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mtree_split_bshrub_r] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "tarantula%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "xnotata%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // create enough files for mdir to split again i = 0; orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "vulsor%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mtree_extend] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "tarantula%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "xnotata%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // switch to early relocations after extending lfs3_unmount(&lfs3) => 0; struct lfs3_cfg cfg = *CFG; cfg.block_recycles = 0; lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "vulsor", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "vulsor") => 0; } // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mtree_extend_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "tarantula%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "xnotata%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // switch to early relocations after extending lfs3_unmount(&lfs3) => 0; struct lfs3_cfg cfg = *CFG; cfg.block_recycles = 0; lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "vulsor", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "vulsor") => 0; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mtree_relocate] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' # force early relocations defines.BLOCK_RECYCLES = 0 code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "vulsor", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "vulsor") => 0; } // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "tarantula%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "xnotata%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // rewrite enough files for mroot to relocate orig = lfs3.mroot.r.blocks[0]; while (lfs3.mroot.r.blocks[0] == orig || lfs3.mroot.r.blocks[0] == orig) { lfs3_file_open(&lfs3, &file, "vulsor", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "vulsor") => 0; } // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mutation_mtree_relocate_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = '2*BLOCK_SIZE' # force early relocations defines.BLOCK_RECYCLES = 0 code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "spider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // rewrite enough files for mroot to extend while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_open(&lfs3, &file, "vulsor", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "vulsor") => 0; } // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "tarantula%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "xnotata%03x", i); lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // try traversing lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroots struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse one data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // rewrite enough files for mroot to relocate orig = lfs3.mroot.r.blocks[0]; while (lfs3.mroot.r.blocks[0] == orig || lfs3.mroot.r.blocks[0] == orig) { lfs3_file_open(&lfs3, &file, "vulsor", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; lfs3_remove(&lfs3, "vulsor") => 0; } // traverse another data block lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse two data blocks lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_DATA); // we should be at end of traversal now if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should _not_ update lookahead/compact/ckmeta/ckdata struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check the file contents lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # test traversals with mdir compaction [cases.test_trvs_compact] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // write to our mdir until >gc_compact_thresh full lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // hack, don't use the internals like this uint8_t wbuf[SIZE]; while ((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file) => 0; } // we should be marked as uncompacted struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing and compacting lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // mdir should have been compacted assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // but because we mutated, we're still marked as uncompacted lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // running another traversal should clear the uncompacted flag lfs3_trv_rewind(&lfs3, &trv) => 0; while (true) { int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // mdir should have been compacted assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // uncompacted flag should have been cleared lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_compact_mrootchain] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' # force early relocations defines.BLOCK_RECYCLES = 0 in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // write to our mdir until mroot extends lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf[SIZE]; while (lfs3.mroot.r.blocks[0] == 0 || lfs3.mroot.r.blocks[0] == 1) { lfs3_file_rewind(&lfs3, &file) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file) => 0; } // now write to our mdir until mrootanchor >gc_compact_thresh full while (true) { // we need internals to check this lfs3_mdir_t mrootanchor; lfs3_mdir_fetch(&lfs3, &mrootanchor, -1, LFS3_MPTR_MROOTANCHOR()) => 0; if (lfs3_rbyd_eoff(&mrootanchor.r) > GC_COMPACT_THRESH) { break; } lfs3_file_rewind(&lfs3, &file) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file) => 0; } // we should be marked as uncompacted struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing and compacting lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mrootanchor struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse mroot lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // mrootanchor should have been compacted lfs3_mdir_t mrootanchor; lfs3_mdir_fetch(&lfs3, &mrootanchor, -1, LFS3_MPTR_MROOTANCHOR()) => 0; assert(lfs3_rbyd_eoff(&mrootanchor.r) <= GC_COMPACT_THRESH); // but because we mutated, we're still marked as uncompacted lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // running another traversal should clear the uncompacted flag lfs3_trv_rewind(&lfs3, &trv) => 0; while (true) { int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // mrootanchor should have been compacted lfs3_mdir_fetch(&lfs3, &mrootanchor, -1, LFS3_MPTR_MROOTANCHOR()) => 0; assert(lfs3_rbyd_eoff(&mrootanchor.r) <= GC_COMPACT_THRESH); // uncompacted flag should have been cleared lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_compact_mroot_extend] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' # force early relocations defines.BLOCK_RECYCLES = 0 in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // write to our mdir until >gc_compact_thresh full lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // hack, don't use the internals like this uint8_t wbuf[SIZE]; while ((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file) => 0; } // we should be marked as uncompacted struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing and compacting lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // it's a bit unclear if we should follow the mroot or stay on the // mroot anchor during extends, so if this breaks in the future // I wouldn't worry too much about it // // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // mdir should have been compacted assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // but because we mutated, we're still marked as uncompacted lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // running another traversal should clear the uncompacted flag lfs3_trv_rewind(&lfs3, &trv) => 0; while (true) { int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // mdir should have been compacted assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // uncompacted flag should have been cleared lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_compact_mroot_split] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "jellyfish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; // create enough files to both compact and split lfs3_size_t i = 0; while (true) { // we should not have split yet assert(lfs3.mtree.r.weight == 0); // we need internals to check this lfs3_ssize_t estimate = lfs3_mdir_estimate___(&lfs3, &file1.b.h.mdir, -1, -1, NULL); assert(estimate >= 0); if ((file1.b.h.mdir.r.eoff & 0x7fffffff) > GC_COMPACT_THRESH && estimate > BLOCK_SIZE/2) { break; } char name[256]; sprintf(name, "medusaaaaaaaaaaaaaaaa%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // we should be marked as uncompacted struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing and compacting lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // should have split, traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // but because we mutated, we're still marked as uncompacted lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // running another traversal should clear the uncompacted flag lfs3_trv_rewind(&lfs3, &trv) => 0; while (true) { int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // uncompacted flag should have been cleared lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "jellyfish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_compact_mtree] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.COMPACTSET = 'range(0x8)' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create three files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "jellyfish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; lfs3_file_t file3; lfs3_file_open(&lfs3, &file3, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file3, wbuf3, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file3) => 0; // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "hydroid%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "medusa%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // write to each file until mdir >gc_compact_thresh full if (COMPACTSET & 0x1) { // hack, don't use the internals like this while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file1) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; } } if (COMPACTSET & 0x2) { // hack, don't use the internals like this while ((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file2) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; } } if (COMPACTSET & 0x4) { // hack, don't use the internals like this while ((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file3) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file3, wbuf3, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file3) => 0; } } // we should be marked as uncompacted struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing and compacting lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; if (COMPACTSET) { // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // but because we mutated, we're still marked as uncompacted lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // running another traversal should clear the uncompacted flag lfs3_trv_rewind(&lfs3, &trv) => 0; while (true) { int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } } lfs3_trv_close(&lfs3, &trv) => 0; // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // uncompacted flag should have been cleared lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_file_close(&lfs3, &file3) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "jellyfish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file3, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_rewind(&lfs3, &file3) => 0; lfs3_file_read(&lfs3, &file3, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_file_close(&lfs3, &file3) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_compact_mtree_split] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create four files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "jellyfish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; lfs3_file_t file3; lfs3_file_open(&lfs3, &file3, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf3[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file3, wbuf3, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file3) => 0; lfs3_file_t file4; lfs3_file_open(&lfs3, &file4, "squid", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf4[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file4, wbuf4, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file4) => 0; // create enough files for mroot to split twice lfs3_size_t i = 0; while (lfs3.mtree.r.weight == 0) { char name[256]; sprintf(name, "hydroid%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } i = 0; lfs3_size_t orig = lfs3.mtree.r.weight; while (lfs3.mtree.r.weight == orig) { char name[256]; sprintf(name, "polyp%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // create enough files to both compact and split i = 0; orig = lfs3.mtree.r.weight; while (true) { // we should not have split yet assert(lfs3.mtree.r.weight == orig); // we need internals to check this lfs3_ssize_t estimate = lfs3_mdir_estimate___(&lfs3, &file2.b.h.mdir, -1, -1, NULL); assert(estimate >= 0); if ((file2.b.h.mdir.r.eoff & 0x7fffffff) > GC_COMPACT_THRESH && estimate > BLOCK_SIZE/2) { break; } char name[256]; sprintf(name, "medusaaaaaaaaaaaaaaaa%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; i += 1; } // we should be marked as uncompacted struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing and compacting lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // should have split, traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse mdir lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file4.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // but because we mutated, we're still marked as uncompacted lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // running another traversal should clear the uncompacted flag lfs3_trv_rewind(&lfs3, &trv) => 0; while (true) { int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file4.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // uncompacted flag should have been cleared lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_file_close(&lfs3, &file3) => 0; lfs3_file_close(&lfs3, &file4) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "jellyfish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file3, "octopus", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file4, "squid", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_rewind(&lfs3, &file3) => 0; lfs3_file_read(&lfs3, &file3, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf3, SIZE) == 0); lfs3_file_rewind(&lfs3, &file4) => 0; lfs3_file_read(&lfs3, &file4, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf4, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_file_close(&lfs3, &file3) => 0; lfs3_file_close(&lfs3, &file4) => 0; lfs3_unmount(&lfs3) => 0; ''' # test traversals with mkconsistent [cases.test_trvs_mkconsistent] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // we should be marked as inconsistent now struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0) | LFS3_I_LOOKAHEAD | ((GBMAP && ORPHANS >= 100) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (ORPHANS > 3) { // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); // which means there shouldn't be that many files left assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits)); assert(file1.b.h.mdir.r.weight <= 3); assert(file2.b.h.mdir.r.weight <= 3); // and we should be marked as consistent lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP && ORPHANS >= 100) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0) | ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mkconsistent_conflict] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; // we should not be marked as inconsistent struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // keep traversing if (ORPHANS <= 3) { // traverse mroot lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); } else { // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should be able to clean up grms assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); // if we introduce actual orphans, me _must not_ clear the orphan flag if (ORPHANS >= 3) { assert(lfs3.flags & LFS3_I_MKCONSISTENT); } // if we introduced actual orphans, we _must_ be marked as inconsistent lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS >= 3) ? LFS3_I_MKCONSISTENT : 0) | ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP && ORPHANS >= 100) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0) | ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mkconsistent_btree] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] # limit files to very simple btrees defines.INLINE_SIZE = 0 defines.CRYSTAL_THRESH = -1 defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8' defines.SIZE = '2*FRAGMENT_SIZE' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // we should be marked as inconsistent now struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0) | LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (ORPHANS <= 3) { // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } else { // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); // which means there shouldn't be that many files left assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits)); assert(file1.b.h.mdir.r.weight <= 3); assert(file2.b.h.mdir.r.weight <= 3); // and we should be marked as consistent lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0) | ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mkconsistent_btree_uncreat] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] # limit files to very simple btrees defines.INLINE_SIZE = 0 defines.CRYSTAL_THRESH = -1 defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8' defines.SIZE = '2*FRAGMENT_SIZE' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // we should be marked as inconsistent now struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0) | LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (ORPHANS <= 3) { // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } else { // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse btree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); // which means there shouldn't be that many files left assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits)); assert(file1.b.h.mdir.r.weight <= 3); assert(file2.b.h.mdir.r.weight <= 3); // and we should be marked as consistent lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0) | ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mkconsistent_bshrub] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] # this configuration should create a 2-layer bshrub, which may be # a bit delicate defines.INLINE_SIZE = 'BLOCK_SIZE/4' defines.CRYSTAL_THRESH = -1 defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8' defines.SIZE = 'BLOCK_SIZE' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // we should be marked as inconsistent now struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0) | LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (ORPHANS <= 3) { // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } else { // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); // which means there shouldn't be that many files left assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits)); assert(file1.b.h.mdir.r.weight <= 3); assert(file2.b.h.mdir.r.weight <= 3); // and we should be marked as consistent lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0) | ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mkconsistent_bshrub_uncreat] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] # this configuration should create a 2-layer bshrub, which may be # a bit delicate defines.INLINE_SIZE = 'BLOCK_SIZE/4' defines.CRYSTAL_THRESH = -1 defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8' defines.SIZE = 'BLOCK_SIZE' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_flush(&lfs3, &file2) => 0; // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // we should be marked as inconsistent now struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0) | LFS3_I_LOOKAHEAD | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (ORPHANS <= 3) { // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } else { // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); // traverse bshrub lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); // which means there shouldn't be that many files left assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits)); assert(file1.b.h.mdir.r.weight <= 3); assert(file2.b.h.mdir.r.weight <= 3); // and we should be marked as consistent lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT // note ckdata implies ckmeta | (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0) | ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mkconsistent_compact] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // write to our mdirs until >gc_compact_thresh full // // hack, don't use the internals like this while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file1) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; } while ((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file2) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; } // we should be marked as inconsistent and uncompacted struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0) | LFS3_I_LOOKAHEAD | ((GBMAP && ORPHANS >= 100) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); if (ORPHANS > 3) { // traverse mtree lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); // which means there shouldn't be that many files left assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits)); assert(file1.b.h.mdir.r.weight <= 3); assert(file2.b.h.mdir.r.weight <= 3); // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // we should be marked as consistent, but because we mutated, we're // still marked as uncompacted lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | ((GBMAP && ORPHANS >= 100) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // running another traversal should clear the uncompacted flag lfs3_trv_rewind(&lfs3, &trv) => 0; while (true) { int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { break; } } lfs3_trv_close(&lfs3, &trv) => 0; // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // uncompacted flag should have been cleared lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0) | ((GBMAP && ORPHANS >= 100) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_mkconsistent_compact_conflict] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FILE_CACHE_SIZE/2' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create two files lfs3_file_t file1; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; lfs3_file_t file2; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; // write to our mdirs until >gc_compact_thresh full // // hack, don't use the internals like this while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file1) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file1) => 0; } while ((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file2) => 0; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE; lfs3_file_sync(&lfs3, &file2) => 0; } // we should not be marked as inconsistent struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // try traversing with mkconsistent lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | LFS3_T_MKCONSISTENT | LFS3_T_COMPACT | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // traverse mroot struct lfs3_tinfo tinfo; lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } // keep traversing if (ORPHANS <= 3) { // traverse mroot lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); } else { // traverse mdirs lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_MDIR); } if (GBMAP) { lfs3_trv_read(&lfs3, &trv, &tinfo) => 0; assert(tinfo.btype == LFS3_BTYPE_BTREE); assert(tinfo.block == 2); } lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT; lfs3_trv_close(&lfs3, &trv) => 0; // we should be able to clean up grms assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); // if we introduce actual orphans, me _must not_ clear the orphan flag if (ORPHANS >= 3) { assert(lfs3.flags & LFS3_I_MKCONSISTENT); } // mdirs should have been compacted assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // if we introduced actual orphans, we _must_ be marked as inconsistent lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((ORPHANS >= 3) ? LFS3_I_MKCONSISTENT : 0) | LFS3_I_LOOKAHEAD | ((GBMAP && ORPHANS >= 100) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0; } lfs3_file_rewind(&lfs3, &file1) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_rewind(&lfs3, &file2) => 0; lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); } lfs3_file_close(&lfs3, &file1) => 0; lfs3_file_close(&lfs3, &file2) => 0; lfs3_unmount(&lfs3) => 0; ''' # many/fuzz tests mixed with traversals # # these should hopefully test a bunch of messy traversal state # [cases.test_trvs_spam_dir_many] # traverse steps between each op defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [true] defines.CKDATA = [true] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256] if = 'GBMAP || !REPOPGBMAP' code = ''' // test creating directories lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // open a traversal lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // make this many directories for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); int err = lfs3_mkdir(&lfs3, name); assert(!err || (TEST_PLS && err == LFS3_ERR_EXIST)); // step the traversal for (lfs3_size_t s = 0; s < STEPS; s++) { struct lfs3_tinfo tinfo; err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); // restart traversal if (err == LFS3_ERR_NOENT) { lfs3_trv_rewind(&lfs3, &trv) => 0; } } } lfs3_trv_close(&lfs3, &trv) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } // grm should be zero here assert(lfs3.grm_p[0] == 0); // check that our mkdir worked for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); lfs3_dir_open(&lfs3, &dir, name) => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; } } lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_spam_dir_fuzz] # traverse steps between each op defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [true] defines.CKDATA = [true] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256] defines.OPS = '2*N' defines.SEED = 42 fuzz = 'SEED' if = 'GBMAP || !REPOPGBMAP' code = ''' // test fuzz with dirs lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // set up a simulation to compare against lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t)); lfs3_size_t sim_size = 0; // open a traversal lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; uint32_t prng = SEED; for (lfs3_size_t i = 0; i < OPS; i++) { // choose a pseudo-random op, either mkdir, remove, or rename uint8_t op = TEST_PRNG(&prng) % 3; if (op == 0 || sim_size == 0) { // choose a pseudo-random number, truncate to 3 hexadecimals lfs3_size_t x = TEST_PRNG(&prng) % N; // insert into our sim for (lfs3_size_t j = 0;; j++) { if (j >= sim_size || sim[j] >= x) { // already seen? if (j < sim_size && sim[j] == x) { // do nothing } else { // insert memmove(&sim[j+1], &sim[j], (sim_size-j)*sizeof(lfs3_size_t)); sim_size += 1; sim[j] = x; } break; } } // create a directory here char name[256]; sprintf(name, "dir%03x", x); int err = lfs3_mkdir(&lfs3, name); assert(!err || err == LFS3_ERR_EXIST); } else if (op == 1) { // choose a pseudo-random entry to delete lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); sim_size -= 1; // remove this directory char name[256]; sprintf(name, "dir%03x", x); lfs3_remove(&lfs3, name) => 0; } else { // choose a pseudo-random entry to rename, and a pseudo-random // number to rename to lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; lfs3_size_t y = TEST_PRNG(&prng) % N; for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // already seen and not a noop? if (k < sim_size && sim[k] == y && x != y) { // just delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); sim_size -= 1; } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); sim[k] = y; } break; } } // rename this directory char old_name[256]; sprintf(old_name, "dir%03x", x); char new_name[256]; sprintf(new_name, "dir%03x", y); lfs3_rename(&lfs3, old_name, new_name) => 0; } // step the traversal for (lfs3_size_t s = 0; s < STEPS; s++) { struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); // restart traversal if (err == LFS3_ERR_NOENT) { lfs3_trv_rewind(&lfs3, &trv) => 0; } } } lfs3_trv_close(&lfs3, &trv) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } // grm should be zero here assert(lfs3.grm_p[0] == 0); // test that our directories match our simulation for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "dir%03x", sim[j]); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; char name2[256]; sprintf(name2, "dir%03x", sim[j]); assert(strcmp(info.name, name2) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "dir%03x", sim[j]); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; } // clean up sim/lfs3 free(sim); lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_spam_file_many] # traverse steps between each op defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [true] defines.CKDATA = [true] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] if = [ '(SIZE*N)/BLOCK_SIZE <= 32', 'GBMAP || !REPOPGBMAP', ] code = ''' // test creating files lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // open a traversal lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; // create this many files uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "amethyst%03x", i); 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_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // step the traversal for (lfs3_size_t s = 0; s < STEPS; s++) { struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); // restart traversal if (err == LFS3_ERR_NOENT) { lfs3_trv_rewind(&lfs3, &trv) => 0; } } } lfs3_trv_close(&lfs3, &trv) => 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 writes worked prng = 42; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "amethyst%03x", 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; ''' [cases.test_trvs_spam_file_fuzz] # traverse steps between each op defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [true] defines.CKDATA = [true] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = '2*N' defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 42 fuzz = 'SEED' if = [ '(SIZE*N)/BLOCK_SIZE <= 16', 'GBMAP || !REPOPGBMAP', ] code = ''' // test fuzz with files lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // set up a simulation to compare against lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); lfs3_size_t sim_size = 0; // open a traversal lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; uint32_t prng = SEED; for (lfs3_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 3; // creating a new file? if (op == 0 || sim_size == 0) { // choose a pseudo-random number lfs3_size_t x = TEST_PRNG(&prng) % N; // associate each file with a prng that generates its contents uint32_t wprng = TEST_PRNG(&prng); // insert into our sim for (lfs3_size_t j = 0;; j++) { if (j >= sim_size || sim[j] >= x) { // already seen? if (j < sim_size && sim[j] == x) { // new prng sim_prngs[j] = wprng; } else { // insert memmove(&sim[j+1], &sim[j], (sim_size-j)*sizeof(lfs3_size_t)); memmove(&sim_prngs[j+1], &sim_prngs[j], (sim_size-j)*sizeof(uint32_t)); sim_size += 1; sim[j] = x; sim_prngs[j] = wprng; } break; } } // create a file here char name[256]; sprintf(name, "amethyst%03x", x); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 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; // deleting a file? } else if (op == 1) { // choose a random file to delete lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); sim_size -= 1; // delete this file char name[256]; sprintf(name, "amethyst%03x", x); lfs3_remove(&lfs3, name) => 0; // renaming a file? } else { // choose a random file to rename, and a random number to // rename to lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; lfs3_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; // update our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng sim_prngs[k] = wprng; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); sim[k] = y; sim_prngs[k] = wprng; } break; } } // rename this file char old_name[256]; sprintf(old_name, "amethyst%03x", x); char new_name[256]; sprintf(new_name, "amethyst%03x", y); lfs3_rename(&lfs3, old_name, new_name) => 0; } // step the traversal for (lfs3_size_t s = 0; s < STEPS; s++) { struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); // restart traversal if (err == LFS3_ERR_NOENT) { lfs3_trv_rewind(&lfs3, &trv) => 0; } } } lfs3_trv_close(&lfs3, &trv) => 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 files match our simulation for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "amethyst%03x", sim[j]); 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); } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "amethyst%03x", sim[j]); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // check the file contents for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "amethyst%03x", sim[j]); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } } // clean up sim/lfs3 free(sim); free(sim_prngs); lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_spam_fwrite_fuzz] # traverse steps between each op defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [true] defines.CKDATA = [true] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.OPS = 20 defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] # chunk is more an upper limit here defines.CHUNK = [32, 8, 1] # INIT=0 => no init # INIT=1 => fill with data # INIT=2 => truncate to size defines.INIT = [0, 1, 2] defines.SYNC = [false, true] defines.SEED = 42 fuzz = 'SEED' if = [ 'CHUNK <= SIZE', # this just saves testing time 'SIZE <= 4*1024*FRAGMENT_SIZE', 'GBMAP || !REPOPGBMAP', ] code = ''' // test with complex file writes lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "hello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // simulate our file in ram uint8_t sim[SIZE]; lfs3_off_t size; uint32_t prng = SEED; if (INIT == 0) { memset(sim, 0, SIZE); size = 0; } else if (INIT == 1) { for (lfs3_size_t i = 0; i < SIZE; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, sim, SIZE) => SIZE; size = SIZE; } else { memset(sim, 0, SIZE); lfs3_file_truncate(&lfs3, &file, SIZE) => 0; size = SIZE; } // sync? if (SYNC) { lfs3_file_sync(&lfs3, &file) => 0; } // open a traversal lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; for (lfs3_size_t i = 0; i < OPS; i++) { // choose a random location lfs3_off_t off = TEST_PRNG(&prng) % SIZE; // and a random size, up to the chunk size lfs3_size_t chunk = lfs3_min( (TEST_PRNG(&prng) % (CHUNK+1-1)) + 1, SIZE - off); // update sim for (lfs3_size_t j = 0; j < chunk; j++) { sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); } size = lfs3_max(size, off+chunk); // update file lfs3_file_seek(&lfs3, &file, off, LFS3_SEEK_SET) => off; lfs3_file_write(&lfs3, &file, &sim[off], chunk) => chunk; // sync? if (SYNC) { lfs3_file_sync(&lfs3, &file) => 0; } // step the traversal for (lfs3_size_t s = 0; s < STEPS; s++) { struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); // restart traversal if (err == LFS3_ERR_NOENT) { lfs3_trv_rewind(&lfs3, &trv) => 0; } } } lfs3_trv_close(&lfs3, &trv) => 0; 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 our file with stat struct lfs3_info info; lfs3_stat(&lfs3, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == size); // and with dir read lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == size); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // try reading our file lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0; // is size correct? lfs3_file_size(&lfs3, &file) => size; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfs3_file_read(&lfs3, &file, rbuf, 2*SIZE) => size; // does our file match our simulation? assert(memcmp(rbuf, sim, size) == 0); lfs3_file_close(&lfs3, &file) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_spam_uz_fuzz] # traverse steps between each op defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [true] defines.CKDATA = [true] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = '2*N' defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 42 fuzz = 'SEED' if = [ '(SIZE*N)/BLOCK_SIZE <= 16', 'GBMAP || !REPOPGBMAP', ] code = ''' // test with uncreats, zombies, etc lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // set up a simulation to compare against lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); bool *sim_isstickys = malloc(N*sizeof(bool)); lfs3_size_t sim_size = 0; typedef struct sim_file { lfs3_size_t x; bool sticky; bool zombie; uint32_t prng; lfs3_file_t file; } sim_file_t; sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); lfs3_size_t sim_file_count = 0; // open a traversal lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; uint32_t prng = SEED; for (lfs3_size_t i = 0; i < OPS; i++) { nonsense:; // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 5; // open a new file? if (op == 0) { if (sim_file_count >= N) { goto nonsense; } // choose a pseudo-random number lfs3_size_t x = TEST_PRNG(&prng) % N; // already exists? bool exist = false; uint32_t wprng = 0; bool sticky = true; for (lfs3_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { exist = true; wprng = sim_prngs[j]; sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist if (!exist) { wprng = TEST_PRNG(&prng); sticky = true; } lfs3_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); // open the actual file char name[256]; sprintf(name, "batman%03x", x); lfs3_file_open(&lfs3, &sim_files[j]->file, name, LFS3_O_RDWR | LFS3_O_CREAT) => 0; // write some initial data if we don't exist if (!exist || sticky) { uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // open in our sim sim_files[j]->x = x; sim_files[j]->sticky = sticky; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; // insert into our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { // new prng sim_prngs[k] = wprng; } else { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; } break; } } // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; // choose a random seed uint32_t wprng = TEST_PRNG(&prng); // write to the file lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0; // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { // update in our sim for (lfs3_size_t k = 0;; k++) { if (sim[k] == x) { // new prng sim_prngs[k] = wprng; // no longer sticky sim_isstickys[k] = false; break; } } // update related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { // new prng sim_files[k]->prng = wprng; // no longer sticky sim_files[k]->sticky = false; } } } // close a file? } else if (op == 2) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; bool sticky = sim_files[j]->sticky; bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies // close the file without affected disk lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0; lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; // clobber closed files to try to catch lingering references memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; // update our sim if (sticky && !zombie) { // orphaned? bool orphan = true; for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { orphan = false; } } // if we were never synced, delete from sim if (orphan) { for (lfs3_size_t k = 0;; k++) { if (sim[k] == x) { memmove(&sim[k], &sim[k+1], (sim_size-(k+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[k], &sim_prngs[k+1], (sim_size-(k+1))*sizeof(uint32_t)); memmove(&sim_isstickys[k], &sim_isstickys[k+1], (sim_size-(k+1))*sizeof(bool)); sim_size -= 1; break; } } } } // remove a file? } else if (op == 3) { if (sim_size == 0) { goto nonsense; } // choose a random file to delete lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; // delete this file char name[256]; sprintf(name, "batman%03x", x); lfs3_remove(&lfs3, name) => 0; // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } // rename a file? } else if (op == 4) { if (sim_size == 0) { goto nonsense; } // choose a random file to rename, and a random number to // rename to lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; lfs3_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; bool sticky = sim_isstickys[j]; // rename this file char old_name[256]; sprintf(old_name, "batman%03x", x); char new_name[256]; sprintf(new_name, "batman%03x", y); lfs3_rename(&lfs3, old_name, new_name) => 0; // update our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng/sticky sim_prngs[k] = wprng; sim_isstickys[k] = sticky; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; } break; } } // update any related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { // move source files if (sim_files[k]->x == x) { sim_files[k]->x = y; // mark target files as zombied } else if (sim_files[k]->x == y) { sim_files[k]->zombie = true; } } } // step the traversal for (lfs3_size_t s = 0; s < STEPS; s++) { struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); // restart traversal if (err == LFS3_ERR_NOENT) { lfs3_trv_rewind(&lfs3, &trv) => 0; } } } lfs3_trv_close(&lfs3, &trv) => 0; // check that disk matches our simulation for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; if (sim_isstickys[j]) { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0; } else { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; } // check that our file handles match our simulation for (lfs3_size_t j = 0; j < sim_file_count; j++) { uint32_t wprng = sim_files[j]->prng; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // clean up sim/lfs3 free(sim); free(sim_prngs); free(sim_isstickys); for (lfs3_size_t j = 0; j < sim_file_count; j++) { lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; free(sim_files[j]); } free(sim_files); lfs3_unmount(&lfs3) => 0; ''' [cases.test_trvs_spam_uzd_fuzz] # traverse steps between each op defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.REPOPGBMAP = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [true] defines.CKDATA = [true] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = '2*N' defines.SIZE = [ '0', 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 42 fuzz = 'SEED' if = [ '(SIZE*N)/BLOCK_SIZE <= 16', 'GBMAP || !REPOPGBMAP', ] code = ''' // test with uncreats, zombies, dirs, etc lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // set up a simulation to compare against lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs3_size_t sim_size = 0; typedef struct sim_file { lfs3_size_t x; bool sticky; bool zombie; uint32_t prng; lfs3_file_t file; } sim_file_t; sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); lfs3_size_t sim_file_count = 0; // open a traversal lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDWR | ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0) | ((REPOPGBMAP) ? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1) : 0) | ((COMPACT) ? LFS3_T_COMPACT : 0) | ((CKMETA) ? LFS3_T_CKMETA : 0) | ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0; uint32_t prng = SEED; for (lfs3_size_t i = 0; i < OPS; i++) { nonsense:; // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 8; // open a new file? if (op == 0) { if (sim_file_count >= N) { goto nonsense; } // choose a pseudo-random number lfs3_size_t x = TEST_PRNG(&prng) % N; // already exists? bool exist = true; uint32_t wprng = 0; bool sticky = true; for (lfs3_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } exist = true; wprng = sim_prngs[j]; sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist if (!exist) { wprng = TEST_PRNG(&prng); sticky = true; } lfs3_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); // open the actual file char name[256]; sprintf(name, "batman%03x", x); lfs3_file_open(&lfs3, &sim_files[j]->file, name, LFS3_O_RDWR | LFS3_O_CREAT) => 0; // write some initial data if we don't exist if (!exist || sticky) { uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // open in our sim sim_files[j]->x = x; sim_files[j]->sticky = sticky; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; // insert into our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { // new prng sim_prngs[k] = wprng; } else { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; sim_isdirs[k] = false; } break; } } // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; // choose a random seed uint32_t wprng = TEST_PRNG(&prng); // write to the file lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0; // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { // update in our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // new prng sim_prngs[k] = wprng; // no longer sticky sim_isstickys[k] = false; break; } } // update related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { // new prng sim_files[k]->prng = wprng; // no longer sticky sim_files[k]->sticky = false; } } } // close a file? } else if (op == 2) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; lfs3_size_t sticky = sim_files[j]->sticky; lfs3_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies // close the file without affected disk lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0; lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; // clobber closed files to try to catch lingering references memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; // update our sim if (sticky && !zombie) { // orphaned? bool orphan = true; for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { orphan = false; } } // if we were never synced, delete from sim if (orphan) { for (lfs3_size_t k = 0;; k++) { if (sim[k] == x) { memmove(&sim[k], &sim[k+1], (sim_size-(k+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[k], &sim_prngs[k+1], (sim_size-(k+1))*sizeof(uint32_t)); memmove(&sim_isstickys[k], &sim_isstickys[k+1], (sim_size-(k+1))*sizeof(bool)); memmove(&sim_isdirs[k], &sim_isdirs[k+1], (sim_size-(k+1))*sizeof(bool)); sim_size -= 1; break; } } } } // remove a file? } else if (op == 3) { if (sim_size == 0) { goto nonsense; } // choose a random file to delete lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; // delete this file char name[256]; sprintf(name, "batman%03x", x); lfs3_remove(&lfs3, name) => 0; // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } // rename a file? } else if (op == 4) { if (sim_size == 0) { goto nonsense; } // choose a random file to rename, and a random number to // rename to lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; lfs3_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; bool sticky = sim_isstickys[j]; bool dir = sim_isdirs[j]; for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // type mismatch? if (sim_isdirs[k] != dir) { goto nonsense; } } break; } } // rename this file char old_name[256]; sprintf(old_name, "batman%03x", x); char new_name[256]; sprintf(new_name, "batman%03x", y); lfs3_rename(&lfs3, old_name, new_name) => 0; // update our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng/sticky/dir sim_prngs[k] = wprng; sim_isstickys[k] = sticky; sim_isdirs[k] = dir; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; sim_isdirs[k] = dir; } break; } } // update any related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { // move source files if (sim_files[k]->x == x) { sim_files[k]->x = y; // mark target files as zombied } else if (sim_files[k]->x == y) { sim_files[k]->zombie = true; } } // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs3_size_t x = TEST_PRNG(&prng) % N; for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; } break; } } // make the directory char name[256]; sprintf(name, "batman%03x", x); lfs3_mkdir(&lfs3, name) => 0; // insert into our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = 0; sim_isdirs[k] = true; break; } } // mark any related sim files as zombied for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } } // step the traversal for (lfs3_size_t s = 0; s < STEPS; s++) { struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); // restart traversal if (err == LFS3_ERR_NOENT) { lfs3_trv_rewind(&lfs3, &trv) => 0; } } } lfs3_trv_close(&lfs3, &trv) => 0; // check that disk matches our simulation for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; for (lfs3_size_t j = 0; j < sim_size; j++) { if (sim_isdirs[j]) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => LFS3_ERR_ISDIR; } else { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; if (sim_isstickys[j]) { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0; } else { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; } } // check that our file handles match our simulation for (lfs3_size_t j = 0; j < sim_file_count; j++) { uint32_t wprng = sim_files[j]->prng; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // clean up sim/lfs3 free(sim); free(sim_prngs); free(sim_isstickys); free(sim_isdirs); for (lfs3_size_t j = 0; j < sim_file_count; j++) { lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; free(sim_files[j]); } free(sim_files); lfs3_unmount(&lfs3) => 0; '''