4d86c90f1b
The tests highlighted that the LFS_I_DIRTY flag in lfsr_tinfo approach
is insufficient. Consider what happens if our filesystem is mutated
while traversing the last mdir:
1. Traversal traverses last mdir, populate blocks, return first block
2. Filesystem mutated, maybe mdir was compacted, clobbers traversal and
sets LFS_I_DIRTY
3. Traversal return LFS_ERR_NOENT immediately, last block never
returned (and out of date), LFS_I_DIRTY never returned
Not only do we miss the LFS_I_DIRTY flag, but we completely miss the
last block in the mdir pair without any warning.
This is _not_ a problem for the actual lookahead buffer, since we still
internally check the LFS_I_DIRTY flag before marking it as complete, but
it is an issue for any external logic that depends on the traversal
being complete...
---
We could revert to LFS_T_EXCL, but, to be honest, I just really don't
know a good name for this flag...
LFS_T_EXCL is a bad name because it conflicts with LFS_O_EXCL. These
flags have very different behaviors, which risks confusing users, and
risks potential name conflicts down the line if we ever want
LFS_T_EXCL-esque semantics for open dirs/files (not unreasonable, though
quite fancy).
My current best contender is LFS_T_WATCH, but while scratching my head
on this, I starting to wonder why we're even providing LFS_T_EXCL in the
first place...
We err on the side of forcing users to implement filesystem-external
features themselves when possible elsewhere, and LFS_T_EXCL technically
_can_ be implemented entirely outside of the filesystem. Though to be
fair it is quite annoying/tedious.
It's not like there's any equivalent feature for dir/file reads anyways.
And a background thread calling lfsr_traversal_read with LFS_T_LOOKAHEAD
will still _eventually_ make progress, even if it takes a bit longer.
Don't get me wrong, I understand it is significantly easier to implement
this inside the filesystem than outside. But it's also easier to
implement this later than right now. And if we implement this later,
hopefully we'll have a better idea what exactly will be useful for
users.
---
Removing LFS_T_EXCL/LFS_I_DIRTY has no real impact on code cost. We were
really just exposing internal logic that we need for lookahead
correctness anyways:
code stack
before: 35224 2680
after: 35220 (-0.0%) 2680 (+0.0%)
8364 lines
273 KiB
TOML
8364 lines
273 KiB
TOML
# Test incremental traversal things
|
|
after = [
|
|
'test_dirs',
|
|
'test_files',
|
|
'test_fwrite',
|
|
'test_forphans',
|
|
'test_alloc'
|
|
]
|
|
|
|
# a simple traversal test
|
|
[cases.test_traversal_simple]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# can we rewind?
|
|
[cases.test_traversal_rewind]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that we don't get extra anything after end of traversal
|
|
[cases.test_traversal_idempotent]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# some simple traversal tests with clobbering
|
|
[cases.test_traversal_clobber_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)) => 0;
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_BTYPE_BTREE);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_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) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_clobber_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)) => 0;
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_BTYPE_BTREE
|
|
|| tinfo.btype == LFS_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_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) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_clobber_open_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfsr_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &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);
|
|
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)) => 0;
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_BTYPE_BTREE
|
|
|| tinfo.btype == LFS_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_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 (lfs_size_t i = 0; i < N; i++) {
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// and everything is fine after saving the files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# a bit more aggressive rewind tests
|
|
[cases.test_traversal_rewind_clobber_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)) => 0;
|
|
lfs_block_t r = 0;
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_BTYPE_BTREE);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_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) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_rewind_clobber_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)) => 0;
|
|
lfs_block_t r = 0;
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_BTYPE_BTREE
|
|
|| tinfo.btype == LFS_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_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) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_rewind_clobber_open_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfsr_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &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);
|
|
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)) => 0;
|
|
lfs_block_t r = 0;
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR
|
|
|| tinfo.btype == LFS_BTYPE_BTREE
|
|
|| tinfo.btype == LFS_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_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 (lfs_size_t i = 0; i < N; i++) {
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// and everything is fine after saving the files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered mdir
|
|
[cases.test_traversal_ckmdir_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKMETA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_traversal_ckmdir_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKMETA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_traversal_ckmdir_open_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfsr_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKMETA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_desync(&lfs, &files[i]) => 0;
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered btree
|
|
[cases.test_traversal_ckbtree_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKMETA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_traversal_ckbtree_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKMETA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_traversal_ckbtree_open_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfsr_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKMETA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_desync(&lfs, &files[i]) => 0;
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered data block
|
|
[cases.test_traversal_ckdata_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
} else {
|
|
k += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKDATA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_traversal_ckdata_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
} else {
|
|
k += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKDATA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_traversal_ckdata_open_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfsr_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
|
lfs_block_t k = 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS_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;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
goto clobbered;
|
|
} else {
|
|
k += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfsr_traversal_open(&lfs, &t, LFS_T_CKDATA) => 0;
|
|
for (lfs_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_desync(&lfs, &files[i]) => 0;
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# test that we detect filesystem mutation during traversal
|
|
[cases.test_traversal_mutation]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 2) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that we don't get extra anything after end of traversal
|
|
[cases.test_traversal_mutation_idempotent]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// mutate
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
|
|
// try another mutation just for good measure
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mkdir]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfsr_mkdir(&lfs, "spider") => 0;
|
|
}
|
|
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfsr_mkdir(&lfs, "spider") => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 2) {
|
|
lfsr_mkdir(&lfs, "spider") => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_rm]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// make a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfsr_remove(&lfs, "spider") => 0;
|
|
}
|
|
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfsr_remove(&lfs, "spider") => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 2) {
|
|
lfsr_remove(&lfs, "spider") => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mv]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// make a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfsr_rename(&lfs, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfsr_rename(&lfs, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 2) {
|
|
lfsr_rename(&lfs, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# some more complex mutation tests
|
|
[cases.test_traversal_mutation_fwrite]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
while (true) {
|
|
// rewrite the file every step of the traversal
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// step traversal
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_fwrite_open]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.SYNC = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
while (true) {
|
|
// rewrite the file every step of the traversal
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// step traversal
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// and after close?
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# test specific cases where we need to clobber traversals
|
|
#
|
|
# these assume quite a bit more and may be a bit fragile...
|
|
#
|
|
[cases.test_traversal_mutation_file_bsprout]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite the file
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY
|
|
| ((TRUNC) ? LFS_O_TRUNC : 0)) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_file_btree]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.INLINE_SIZE = 0
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY
|
|
| ((TRUNC) ? LFS_O_TRUNC : 0)) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_file_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY
|
|
| ((TRUNC) ? LFS_O_TRUNC : 0)) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_orphan_bsprout]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite the file
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_orphan_btree]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.INLINE_SIZE = 0
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_orphan_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_close_bsprout]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfsr_file_desync(&lfs, &file1) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_t file;
|
|
if (DESYNC) {
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
} else {
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_close_btree]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.INLINE_SIZE = 0
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfsr_file_desync(&lfs, &file1) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_t file;
|
|
if (DESYNC) {
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
} else {
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_close_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfsr_file_desync(&lfs, &file1) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_t file;
|
|
if (DESYNC) {
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
} else {
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_rm_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "spider") => 0;
|
|
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mv_src_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rename one file over another
|
|
lfsr_rename(&lfs, "spider", "tarantula") => 0;
|
|
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mv_dst_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rename one file over another
|
|
lfsr_rename(&lfs, "tarantula", "spider") => 0;
|
|
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mroot_split]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create enough files for mroot to split
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mroot_split_bshrub_l]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse a data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// create enough files for mroot to split
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mroot_split_bshrub_r]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "zodarion",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// and another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// create enough files for mroot to split
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "zodarion", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mroot_extend]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "uloborus") => 0;
|
|
}
|
|
|
|
// traverse mroot
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mroot_extend_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse a data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "uloborus") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mroot_relocate]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "uloborus") => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
lfs_block_t orig = lfs.mroot.rbyd.blocks[0];
|
|
while (lfs.mroot.rbyd.blocks[0] == orig
|
|
|| lfs.mroot.rbyd.blocks[0] == orig) {
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "uloborus") => 0;
|
|
}
|
|
|
|
// it's a bit unclear if clobbered mroot chain traversals should
|
|
// still traverse inlined mroots, so if this breaks in the future
|
|
// I wouldn't worry too much about it
|
|
//
|
|
// traverse mroot
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mroot_relocate_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "uloborus") => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroots
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse a data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
lfs_block_t orig = lfs.mroot.rbyd.blocks[0];
|
|
while (lfs.mroot.rbyd.blocks[0] == orig
|
|
|| lfs.mroot.rbyd.blocks[0] == orig) {
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "uloborus") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mtree_split]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mtree_split_bshrub_l]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mtree_split_bshrub_r]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mtree_extend]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// switch to early relocations after extending
|
|
lfsr_unmount(&lfs) => 0;
|
|
struct lfs_config cfg = *CFG;
|
|
cfg.block_recycles = 0;
|
|
lfsr_mount(&lfs, &cfg) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "vulsor",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mtree_extend_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// switch to early relocations after extending
|
|
lfsr_unmount(&lfs) => 0;
|
|
struct lfs_config cfg = *CFG;
|
|
cfg.block_recycles = 0;
|
|
lfsr_mount(&lfs, &cfg) => 0;
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "vulsor",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mtree_relocate]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "vulsor",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "vulsor") => 0;
|
|
}
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
orig = lfs.mroot.rbyd.blocks[0];
|
|
while (lfs.mroot.rbyd.blocks[0] == orig
|
|
|| lfs.mroot.rbyd.blocks[0] == orig) {
|
|
lfsr_file_open(&lfs, &file, "vulsor",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_mutation_mtree_relocate_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "spider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_open(&lfs, &file, "vulsor",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "vulsor") => 0;
|
|
}
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
|
|
// traverse mroots
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse mtree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
orig = lfs.mroot.rbyd.blocks[0];
|
|
while (lfs.mroot.rbyd.blocks[0] == orig
|
|
|| lfs.mroot.rbyd.blocks[0] == orig) {
|
|
lfsr_file_open(&lfs, &file, "vulsor",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_remove(&lfs, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check the file contents
|
|
lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# test traversals with mdir compaction
|
|
|
|
[cases.test_traversal_compact]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// write to our mdir until >gc_compact_thresh full
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// hack, don't use the internals like this
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_mrootchain]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// write to our mdir until mroot extends
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
uint8_t wbuf[SIZE];
|
|
while (lfs.mroot.rbyd.blocks[0] == 0
|
|
|| lfs.mroot.rbyd.blocks[0] == 1) {
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// now write to our mdir until mrootanchor >gc_compact_thresh full
|
|
while (true) {
|
|
// we need internals to check this
|
|
lfsr_mdir_t mrootanchor;
|
|
lfsr_mdir_fetch(&lfs, &mrootanchor,
|
|
-1, &LFSR_MPTR_MROOTANCHOR()) => 0;
|
|
if (lfsr_rbyd_eoff(&mrootanchor.rbyd) > GC_COMPACT_THRESH) {
|
|
break;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mrootanchor
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mroot
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// mrootanchor should have been compacted
|
|
lfsr_mdir_t mrootanchor;
|
|
lfsr_mdir_fetch(&lfs, &mrootanchor,
|
|
-1, &LFSR_MPTR_MROOTANCHOR()) => 0;
|
|
assert(lfsr_rbyd_eoff(&mrootanchor.rbyd) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_mroot_extend]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// write to our mdir until >gc_compact_thresh full
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// hack, don't use the internals like this
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 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 lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_mroot_split]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file1) => 0;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "octopus",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file2) => 0;
|
|
|
|
// create enough files to both compact and split
|
|
lfs_size_t i = 0;
|
|
while (true) {
|
|
// we need internals to check this
|
|
lfs_ssize_t estimate = lfsr_mdir_estimate__(&lfs,
|
|
&file1.o.o.mdir, -1, -1,
|
|
NULL);
|
|
assert(estimate >= 0);
|
|
if ((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) > GC_COMPACT_THRESH
|
|
&& estimate > BLOCK_SIZE/2) {
|
|
break;
|
|
}
|
|
|
|
char name[256];
|
|
sprintf(name, "medusaaaaaaaaaaaaaaaaaaaaaaaaaaaa%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// should have split, traverse mtree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file2) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_mtree]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.COMPACTSET = 'range(0x7)'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "cuttlefish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file1) => 0;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file2) => 0;
|
|
|
|
lfsr_file_t file3;
|
|
lfsr_file_open(&lfs, &file3, "octopus",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file3) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "hydroid%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "medusa%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &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.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file1) => 0;
|
|
}
|
|
}
|
|
|
|
if (COMPACTSET & 0x2) {
|
|
// hack, don't use the internals like this
|
|
while ((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file2) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file2) => 0;
|
|
}
|
|
}
|
|
|
|
if (COMPACTSET & 0x4) {
|
|
// hack, don't use the internals like this
|
|
while ((file3.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file3) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file3) => 0;
|
|
}
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file3.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_close(&lfs, &file3) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file1, "cuttlefish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &file3, "octopus", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file2) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file3) => 0;
|
|
lfsr_file_read(&lfs, &file3, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_close(&lfs, &file3) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_mtree_split]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "cuttlefish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file1) => 0;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file2) => 0;
|
|
|
|
lfsr_file_t file3;
|
|
lfsr_file_open(&lfs, &file3, "octopus",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file3) => 0;
|
|
|
|
lfsr_file_t file4;
|
|
lfsr_file_open(&lfs, &file4, "squid",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file4, wbuf4, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file4) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "hydroid%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs_size_t orig = lfs.mtree.u.weight;
|
|
while (lfs.mtree.u.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "polyp%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// create enough files to both compact and split
|
|
i = 0;
|
|
while (true) {
|
|
// we need internals to check this
|
|
lfs_ssize_t estimate = lfsr_mdir_estimate__(&lfs,
|
|
&file2.o.o.mdir, -1, -1,
|
|
NULL);
|
|
assert(estimate >= 0);
|
|
if ((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) > GC_COMPACT_THRESH
|
|
&& estimate > BLOCK_SIZE/2) {
|
|
break;
|
|
}
|
|
|
|
char name[256];
|
|
sprintf(name, "medusaaaaaaaaaaaaaaaaaaaaaaaaaaaa%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// should have split, traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file3.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file4.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_close(&lfs, &file3) => 0;
|
|
lfsr_file_close(&lfs, &file4) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file1, "cuttlefish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &file3, "octopus", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &file4, "squid", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file2) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file3) => 0;
|
|
lfsr_file_read(&lfs, &file3, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file4) => 0;
|
|
lfsr_file_read(&lfs, &file4, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_close(&lfs, &file3) => 0;
|
|
lfsr_file_close(&lfs, &file4) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# btree/bshrub compactions are quite a bit more difficult
|
|
|
|
[cases.test_traversal_compact_mtree_btree]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file1) => 0;
|
|
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "octopus",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file2) => 0;
|
|
|
|
// create enough files for mroot to split
|
|
lfs_size_t i = 0;
|
|
while (lfs.mtree.u.weight == 0x80000000) {
|
|
char name[256];
|
|
sprintf(name, "medusa%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// switch to early relocations after splitting
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
struct lfs_config cfg = *CFG;
|
|
cfg.block_recycles = 0;
|
|
lfsr_mount(&lfs, &cfg) => 0;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDWR) => 0;
|
|
lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDWR) => 0;
|
|
|
|
// rewrite a file until btree is >gc_compact_thresh full
|
|
while (true) {
|
|
// we need internals to check this
|
|
// ckmeta needed for eoff
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
if (lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
> GC_COMPACT_THRESH) {
|
|
break;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file1) => 0;
|
|
}
|
|
|
|
// switch back to default relocations to avoid mroot extension issues
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDWR) => 0;
|
|
lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDWR) => 0;
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// mtree should have been compacted
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
<= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file2) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_btree]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.INLINE_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// rewrite our file until btree is >gc_compact_thresh full
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// btree should have been compacted
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_btree_open]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.INLINE_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// rewrite our file until btree is >gc_compact_thresh full
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// btree should have been compacted
|
|
assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_btree_orphan]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.INLINE_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// rewrite our file until btree is >gc_compact_thresh full
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// btree should have been compacted
|
|
assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// file should not have accidentally been created or anything
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "jellyfish", &info) => LFS_ERR_NOENT;
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_btree_desync]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.INLINE_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a desync file
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
|
|
// rewrite our file until btree is >gc_compact_thresh full
|
|
uint8_t wbuf1[SIZE];
|
|
while ((file1.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
}
|
|
|
|
// create some overlapping files, these should not get messed with
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file2) => 0;
|
|
|
|
lfsr_file_t file3;
|
|
lfsr_file_open(&lfs, &file3, "jellyfish",
|
|
LFS_O_RDWR) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_desync(&lfs, &file3) => 0;
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
// traverse btree
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// btree should have been compacted
|
|
assert((file1.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// check we can still read the files
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file2) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file3) => 0;
|
|
lfsr_file_read(&lfs, &file3, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
|
|
// at least try closing/opening our synced file
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_close(&lfs, &file3) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_bshrub]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
|
|
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
|
while (true) {
|
|
// we need internals to check this
|
|
// ckmeta needed for eoff
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
if (lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
> GC_COMPACT_THRESH) {
|
|
break;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// compacting our bshrub nodes may cause them to split, so we may
|
|
// need to traverse more nodes than we started with
|
|
for (lfs_size_t i = 0; i < 5; i++) {
|
|
// traverse bshrub
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
}
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// bshrub should have been compacted
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
while (true) {
|
|
int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
<= GC_COMPACT_THRESH);
|
|
}
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_bshrub_open]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
|
|
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
|
while (true) {
|
|
// we need internals to check this
|
|
// ckmeta needed for eoff
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
if (lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
> GC_COMPACT_THRESH) {
|
|
break;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
|
}
|
|
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// compacting our bshrub nodes may cause them to split, so we may
|
|
// need to traverse more nodes than we started with
|
|
for (lfs_size_t i = 0; i < 5; i++) {
|
|
// traverse bshrub
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
}
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// bshrub should have been compacted
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
while (true) {
|
|
int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
<= GC_COMPACT_THRESH);
|
|
}
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_bshrub_orphan]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
|
|
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
|
while (true) {
|
|
// we need internals to check this
|
|
// ckmeta needed for eoff
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
if (lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
> GC_COMPACT_THRESH) {
|
|
break;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
|
}
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// compacting our bshrub nodes may cause them to split, so we may
|
|
// need to traverse more nodes than we started with
|
|
for (lfs_size_t i = 0; i < 5; i++) {
|
|
// traverse bshrub
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
}
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// bshrub should have been compacted
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
while (true) {
|
|
int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
<= GC_COMPACT_THRESH);
|
|
}
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_compact_bshrub_desync]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a desync file
|
|
lfsr_file_t file1;
|
|
lfsr_file_open(&lfs, &file1, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
|
|
|
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
|
while (true) {
|
|
// we need internals to check this
|
|
// ckmeta needed for eoff
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
if (lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
> GC_COMPACT_THRESH) {
|
|
break;
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file1, wbuf1, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
|
}
|
|
|
|
// create some overlapping files, these should not get messed with
|
|
lfsr_file_t file2;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &file2) => 0;
|
|
|
|
lfsr_file_t file3;
|
|
lfsr_file_open(&lfs, &file3, "jellyfish",
|
|
LFS_O_RDWR) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE;
|
|
lfsr_file_desync(&lfs, &file3) => 0;
|
|
|
|
// try traversing and compacting
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
LFS_T_COMPACT
|
|
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs_tinfo tinfo;
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// compacting our bshrub nodes may cause them to split, so we may
|
|
// need to traverse more nodes than we started with
|
|
for (lfs_size_t i = 0; i < 3*5; i++) {
|
|
// traverse bshrub
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
|
}
|
|
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// bshrub should have been compacted
|
|
lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA);
|
|
lfsr_tag_t tag;
|
|
lfsr_bptr_t bptr;
|
|
lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0;
|
|
assert(tag == LFSR_TAG_MDIR);
|
|
while (true) {
|
|
int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(tag == LFSR_TAG_BRANCH);
|
|
assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer)
|
|
<= GC_COMPACT_THRESH);
|
|
}
|
|
|
|
// check we can still read the files
|
|
lfsr_file_rewind(&lfs, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file2) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfsr_file_rewind(&lfs, &file3) => 0;
|
|
lfsr_file_read(&lfs, &file3, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
|
|
// at least try closing/opening our synced file
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_open(&lfs, &file2, "jellyfish", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfsr_file_close(&lfs, &file1) => 0;
|
|
lfsr_file_close(&lfs, &file2) => 0;
|
|
lfsr_file_close(&lfs, &file3) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
|
|
# many/fuzz tests mixed with traversals
|
|
#
|
|
# these should hopefully test a bunch of messy traversal state
|
|
#
|
|
|
|
[cases.test_traversal_spam_dir_many]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, 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
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// open a traversal
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
|
|
// make this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || (TEST_PLS && err == LFS_ERR_EXIST));
|
|
|
|
// step the traversal
|
|
for (lfs_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs_tinfo tinfo;
|
|
err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// check that our mkdir worked
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_open(&lfs, &dir, name) => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_spam_dir_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, 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
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
// open a traversal
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_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
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
// insert into our sim
|
|
for (lfs_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(lfs_size_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a directory here
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || err == LFS_ERR_EXIST);
|
|
|
|
} else if (op == 1) {
|
|
// choose a pseudo-random entry to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
|
|
// remove this directory
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
} else {
|
|
// choose a pseudo-random entry to rename, and a pseudo-random
|
|
// number to rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
for (lfs_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(lfs_size_t));
|
|
sim_size -= 1;
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_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);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// test that our directories match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
char name2[256];
|
|
sprintf(name2, "dir%03x", sim[j]);
|
|
assert(strcmp(info.name, name2) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_spam_file_many]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
// test creating files
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// open a traversal
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// step the traversal
|
|
for (lfs_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that our writes worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_spam_file_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_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
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
// open a traversal
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_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
|
|
lfs_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 (lfs_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(lfs_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 (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_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);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_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(lfs_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(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_spam_fwrite_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.OPS = 20
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_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
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "hello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
// simulate our file in ram
|
|
uint8_t sim[SIZE];
|
|
lfs_off_t size;
|
|
uint32_t prng = SEED;
|
|
if (INIT == 0) {
|
|
memset(sim, 0, SIZE);
|
|
size = 0;
|
|
} else if (INIT == 1) {
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
|
size = SIZE;
|
|
} else {
|
|
memset(sim, 0, SIZE);
|
|
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
|
size = SIZE;
|
|
}
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// open a traversal
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose a random location
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
// and a random size, up to the chunk size
|
|
lfs_size_t chunk = lfs_min(
|
|
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
|
|
SIZE - off);
|
|
|
|
// update sim
|
|
for (lfs_size_t j = 0; j < chunk; j++) {
|
|
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
size = lfs_max(size, off+chunk);
|
|
|
|
// update file
|
|
lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check our file with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
|
|
// and with dir read
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// try reading our file
|
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => size;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size;
|
|
// does our file match our simulation?
|
|
assert(memcmp(rbuf, sim, size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_spam_orphanzombie_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_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 orphans, zombies, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
// open a traversal
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_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
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_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(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// 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
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// 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
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_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(lfs_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(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_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;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_traversal_spam_orphanzombiedir_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_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 orphans, zombies, dirs, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isdirs = malloc(N*sizeof(bool));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
// open a traversal
|
|
lfsr_traversal_t t;
|
|
lfsr_traversal_open(&lfs, &t,
|
|
((CKMETA) ? LFS_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS_T_CKDATA : 0)
|
|
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)
|
|
| ((COMPACT) ? LFS_T_COMPACT : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_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
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_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(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// 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
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
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 (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// 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
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool isdir = sim_isdirs[j];
|
|
|
|
// update our sim
|
|
for (lfs_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] != isdir) {
|
|
goto nonsense;
|
|
}
|
|
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
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
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = isdir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_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;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
|
|
// toss a directory into the mix
|
|
} else if (op == 5) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// insert into our sim, use negative numbers for dirs
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
goto nonsense;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
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 (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// make the directory
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs_tinfo tinfo;
|
|
int err = lfsr_traversal_read(&lfs, &t, &tinfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS_ERR_NOENT) {
|
|
lfsr_traversal_rewind(&lfs, &t) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfsr_traversal_close(&lfs, &t) => 0;
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim_isdirs[j]) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
|
|
} else {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|