Files
littlefs/tests/test_trvs.toml
Christopher Haster bc9562c64e tests: Accidentally found a couple buffer overruns
- Off-by-one in test_btree_find_general[_sparse]_fuzz

  Because we can only create named btrees via splitting, these always
  start with one entry. If all operations are randomly selected to be
  splits, this can lead to an overflow of the sim buffer (sounds
  unlikely, but relatively easily for small N).

  The fix is to use a `for (lfs3_size_t i = 1; i < N; i++)` loop to
  account for the initial entry. Note we already use this in the
  test_btree_split_* tests.

  An alternative is allocating space for N+1 entries, but this seems
  unintuitive with N usually being associated with the upper bound on
  btree size.

- Off-by-one in our sim rename pattern

  When renaming, we don't bother to update sim_size, because after the
  rename the sim_size size will be unchanged. But this means the
  sim_size is out-of-date during the memmove that reinserts the renamed
  entry. Buffer overflow!

  To fix we just need to use sim_size-1 to account for the temporarily
  deleted entry.

  This is messy C code, so not surprised it went unnoticed, even though
  this pattern ended up in quite a few tests.

Found while running with HEAP=1. This was just intended to test HEAP=1,
but I guess the injected heap hooks result in a more fragile heap? They
increase all allocations by one word, and maybe this reduces alignment
padding? Not exactly sure.

But it's a good argument for maybe adding heap canaries in the future.
Previously we ran Valgrind on all tests, but it's unclear if this will
still be reasonable with the number of tests we have now.
2026-02-19 12:39:20 -06:00

11115 lines
378 KiB
TOML

# 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_IFYES_GBMAP(GBMAP, true, !GBMAP)'
# a simple traversal test
[cases.test_trvs_simple]
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)
| ((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);
// traverse gbmap
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)
| ((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);
// traverse gbmap
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);
// traverse gbmap
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.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)
| ((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);
// traverse gbmap
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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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',
'FCACHE_SIZE/2',
'2*FCACHE_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.PREERASE = [false, true]
defines.COMPACT = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
if = [
'LFS3_IFDEF_PREERASE(true, !PREERASE)',
'GBMAP || !PREERASE',
]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3,
LFS3_F_RDWR
// note preerasing needs revperturb
| ((PREERASE)
? LFS3_IFDEF_PREERASE(LFS3_F_REVPERTURB, -1)
: 0)
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
// note preerasing needs revperturb
| ((PREERASE)
? LFS3_IFDEF_PREERASE(LFS3_M_REVPERTURB, -1)
: 0),
CFG) => 0;
// check flags before
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(
LFS3_I_REVPERTURB,
(PREERASE) ? LFS3_I_REVPERTURB : 0,
0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_MKCONSISTENT
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| 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)
| ((PREERASE)
? LFS3_IFDEF_PREERASE(LFS3_T_PREERASE, -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);
// traverse gbmap
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 == (
LFS3_IFYES_REVPERTURB(
LFS3_I_REVPERTURB,
(PREERASE) ? LFS3_I_REVPERTURB : 0,
0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!MKCONSISTENT) ? LFS3_I_MKCONSISTENT : 0)
| ((!LOOKAHEAD && !GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
// terminate early?
if (EXCL && WHEN == 0) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
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;
}
// terminate early?
if (EXCL && WHEN == 1) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
if (WHEN != 1) {
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;
}
// terminate early?
if (EXCL && WHEN == 2) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse gbmap
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;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
} else {
// traverse gbmap
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
} else {
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
// terminate early?
if (EXCL && WHEN == 0) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
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;
}
// terminate early?
if (EXCL && WHEN == 1) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
if (WHEN != 1) {
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;
}
// terminate early?
if (EXCL && WHEN == 2) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse gbmap
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;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
// terminate early?
if (EXCL && WHEN == 0) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
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;
}
// terminate early?
if (EXCL && WHEN == 1) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
if (WHEN != 1) {
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;
}
// terminate early?
if (EXCL && WHEN == 2) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse gbmap
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;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
// terminate early?
if (EXCL && WHEN == 0) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
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;
}
// terminate early?
if (EXCL && WHEN == 1) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
if (WHEN != 1) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
// traverse gbmap
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;
}
// terminate early?
if (EXCL && WHEN == 2) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
if (WHEN == 2) {
// traverse gbmap again
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;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| 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.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
| ((CKMETA) ? LFS3_T_CKMETA : 0)
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
// mutating traversals may never terminate, so stop at some point
for (lfs3_size_t i = 0; i < 4*BLOCK_COUNT; i++) {
// 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
|| (EXCL && err == LFS3_ERR_BUSY));
if (err == LFS3_ERR_NOENT
|| err == LFS3_ERR_BUSY) {
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP || SIZE >= BLOCK_SIZE/4)
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
| ((CKMETA) ? LFS3_T_CKMETA : 0)
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
// mutating traversals may never terminate, so stop at some point
for (lfs3_size_t i = 0; i < 4*BLOCK_COUNT; i++) {
// 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
|| (EXCL && err == LFS3_ERR_BUSY));
if (err == LFS3_ERR_NOENT
|| err == LFS3_ERR_BUSY) {
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
// ok we might've updated lookahead if write stayed in
// cache
| (((!LOOKAHEAD || SIZE > FCACHE_SIZE || SYNC)
&& (!GBMAP || SIZE >= BLOCK_SIZE/4))
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 0)
| LFS3_I_COMPACT
// and maybe ckmeta/ckdata, note ckdata implies ckmeta
| (((!CKMETA && !CKDATA) || SIZE > FCACHE_SIZE || SYNC)
? LFS3_I_CKMETA
: 0)
| ((!CKDATA || SIZE > FCACHE_SIZE || SYNC)
? LFS3_I_CKDATA
: 0)
| ((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.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = 'FCACHE_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// we should be at end of traversal now
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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_btree]
defines.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = '2*BLOCK_SIZE'
defines.SHRUB_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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);
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = 'FCACHE_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// we should be at end of traversal now
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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_btree]
defines.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = '2*BLOCK_SIZE'
defines.SHRUB_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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);
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = 'FCACHE_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// close the file
if (DESYNC) {
lfs3_file_desync(&lfs3, &file1) => 0;
}
lfs3_file_close(&lfs3, &file1) => 0;
// if not desync, traversal rewinds to mdir
if (!DESYNC) {
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse gbmap again
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
}
// we should be at end of traversal now
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/etc, unless we're desynced
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
| ((!(LOOKAHEAD && DESYNC) && !GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = '2*BLOCK_SIZE'
defines.SHRUB_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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// if not desync, traversal rewinds to mdir
if (!DESYNC) {
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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);
}
// traverse another data block
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/etc, unless we're desynced
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
| ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// if not desync, traversal rewinds to mdir
if (!DESYNC) {
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse one data block
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_DATA);
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/etc, unless we're desynced
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
| ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/etc, unless we're desynced
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
| ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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 an 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.EXCL = [false, true]
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
| ((EXCL) ? LFS3_T_EXCL : 0)
| ((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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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;
}
// terminate early?
if (EXCL) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
lfs3_trv_close(&lfs3, &trv) => 0;
goto done;
}
// 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
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
lfs3_trv_close(&lfs3, &trv) => 0;
done:;
// we should _not_ update lookahead/compact/ckmeta/ckdata
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD && !GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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_mrootanchor]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = 'FCACHE_SIZE/2'
# set compact thresh to minimum
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
# force early relocations, but keep at least 1 to prevent mroot anchor
# from extendeding during compaction
defines.BLOCK_RECYCLES = 1
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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);
// traverse gbmap
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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_mrootanchor_extend]
defines.LOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.SIZE = 'FCACHE_SIZE/2'
# set compact thresh to minimum
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
# force early relocations, this will cause the mroot anchor to extend
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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, trigger compaction, no longer 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);
// traverse gbmap
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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;
// 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);
// traverse gbmap
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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);
// should have split, traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
| ((!GBMAP || ORPHANS >= 100) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
} else {
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| (((!LOOKAHEAD || ORPHANS > 0)
&& (!GBMAP || ORPHANS >= 100))
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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);
// 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) {
if (ORPHANS == 0) {
// traverse mroot
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
} else {
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS >= 3) ? LFS3_I_MKCONSISTENT : 0)
| (((!LOOKAHEAD || ORPHANS > 0)
&& (!GBMAP || ORPHANS >= 100))
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.SHRUB_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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.SHRUB_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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.SHRUB_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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.SHRUB_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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
| LFS3_I_LOOKAHEAD
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
| ((!GBMAP || ORPHANS >= 100)
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
} else {
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP || ORPHANS >= 100)
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!LOOKAHEAD && (!GBMAP || ORPHANS >= 100))
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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 = 'FCACHE_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_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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);
// 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) {
if (ORPHANS == 0) {
// traverse mroot
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_MDIR);
assert(tinfo.block == 0 || tinfo.block == 1);
}
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
} else {
// traverse mtree
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
// traverse gbmap
if (GBMAP) {
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
assert(tinfo.btype == LFS3_BTYPE_BTREE);
assert(tinfo.block == 2);
}
// 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);
}
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 == (
LFS3_IFYES_REVPERTURB(LFS3_I_REVPERTURB, 0, 0)
| LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0)
| ((ORPHANS >= 3) ? LFS3_I_MKCONSISTENT : 0)
| ((!GBMAP || ORPHANS >= 100)
? LFS3_I_LOOKAHEAD
: 0)
| LFS3_IFDEF_PREERASE((GBMAP) ? LFS3_I_PREERASE : 0, 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.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]
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)
| ((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.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'
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)
| ((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-1-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.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',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
if = '(SIZE*N)/BLOCK_SIZE <= 32'
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)
| ((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.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',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.SEED = 42
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
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)
| ((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-1-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-1-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.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 = [
'FCACHE_SIZE/2',
'2*FCACHE_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',
]
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)
| ((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.COMPACT = [false, true]
defines.CKMETA = [true]
defines.CKDATA = [true]
# set compact thresh to minimum
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
# you probably need to flush if you expect errors
defines.FLUSH = false
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.OPS = '2*N'
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.SEED = 42
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
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)
| ((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
| ((FLUSH) ? LFS3_O_FLUSH : 0)) => 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-1-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-1-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-1-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.COMPACT = [false, true]
defines.CKMETA = [true]
defines.CKDATA = [true]
# set compact thresh to minimum
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
# you probably need to flush if you expect errors
defines.FLUSH = false
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.OPS = '2*N'
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.SEED = 42
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
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)
| ((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
| ((FLUSH) ? LFS3_O_FLUSH : 0)) => 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-1-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-1-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-1-k)*sizeof(bool));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-1-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;
'''