mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-12-01 12:20:02 +00:00
And tweaked a few related comments. I'm still on the fence with this name, I don't think it's great, but it at least betters describes the "repopulation" operation than "rebuilding". The important distinction is that we don't throw away information. Bad/erased block info (future) is still carried over into the new gbmap snapshot, and persists unless you explicitly call rmgbmap + mkgbmap. So, adopting gbmap_repop_thresh for now to see if it's just a habit thing, but may adopt a different name in the future. As a plus, gbmap_repop_thresh is two characters shorter.
10262 lines
346 KiB
TOML
10262 lines
346 KiB
TOML
# Test incremental traversal things
|
|
after = [
|
|
'test_dirs',
|
|
'test_files',
|
|
'test_fwrite',
|
|
'test_stickynotes',
|
|
'test_alloc'
|
|
]
|
|
|
|
# Test both with and without the gbmap if available
|
|
defines.GBMAP = [false, true]
|
|
if = '''
|
|
LFS3_IFDEF_YES_GBMAP(
|
|
GBMAP,
|
|
LFS3_IFDEF_GBMAP(true, !GBMAP))
|
|
'''
|
|
|
|
|
|
# a simple traversal test
|
|
[cases.test_trvs_simple]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = 'GBMAP || !REPOPGBMAP'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# can we rewind?
|
|
[cases.test_trvs_rewind]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we don't get extra anything after end of traversal
|
|
[cases.test_trvs_idempotent]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = 'GBMAP || !REPOPGBMAP'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# some simple traversal tests with clobbering
|
|
[cases.test_trvs_clobber_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that we can read our directories after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_clobber_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_clobber_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_rewind(&lfs3, &files[i]) => 0;
|
|
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// and everything is fine after saving the files
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# a bit more aggressive rewind tests
|
|
[cases.test_trvs_rewind_clobber_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
lfs3_block_t r = 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that we can read our directories after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_rewind_clobber_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
lfs3_block_t r = 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_rewind_clobber_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
lfs3_block_t r = 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_rewind(&lfs3, &files[i]) => 0;
|
|
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// and everything is fine after saving the files
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered mdir
|
|
[cases.test_trvs_ckmdir_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (k == i+1) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckmdir_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (k == i+1) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckmdir_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (k == i+1) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_desync(&lfs3, &files[i]) => 0;
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered btree
|
|
[cases.test_trvs_ckbtree_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_BTREE) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckbtree_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_BTREE) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckbtree_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_BTREE) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_desync(&lfs3, &files[i]) => 0;
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered data block
|
|
[cases.test_trvs_ckdata_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_DATA) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckdata_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_DATA) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckdata_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_DATA) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_desync(&lfs3, &files[i]) => 0;
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# test that in general fsinfo flags work
|
|
[cases.test_trvs_flags]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = 'GBMAP || !REPOPGBMAP'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// check flags before
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_MKCONSISTENT
|
|
| LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// check flags after
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!MKCONSISTENT) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((!COMPACT) ? LFS3_I_COMPACT : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we detect filesystem mutation during traversal
|
|
[cases.test_trvs_mutation]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we don't get extra anything after end of traversal
|
|
[cases.test_trvs_mutation_idempotent]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// mutate
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try another mutation just for good measure
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mkdir]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_mkdir(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_mkdir(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_mkdir(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_rm]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// make a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mv]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// make a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# some more complex mutation tests
|
|
[cases.test_trvs_mutation_fwrite]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
while (true) {
|
|
// rewrite the file every step of the traversal
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// step traversal
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP && SIZE >= BLOCK_SIZE/4)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_fwrite_opened]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.SYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file) => 0;
|
|
if (SYNC) {
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
while (true) {
|
|
// rewrite the file every step of the traversal
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
if (SYNC) {
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// step traversal
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
// ok we might've updated lookahead if write stayed in cache
|
|
((!LOOKAHEAD || SIZE > FILE_CACHE_SIZE || SYNC)
|
|
? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP && SIZE >= BLOCK_SIZE/4)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// and after close?
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
# test specific cases where we need to clobber traversals
|
|
#
|
|
# these assume quite a bit more and may be a bit fragile...
|
|
#
|
|
[cases.test_trvs_mutation_file_bsprout]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite the file
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY
|
|
| ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_file_btree]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.INLINE_SIZE = 0
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY
|
|
| ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_file_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY
|
|
| ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_uncreat_bsprout]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite the file
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_uncreat_btree]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.INLINE_SIZE = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_uncreat_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_close_bsprout]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfs3_file_desync(&lfs3, &file1) => 0;
|
|
}
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
if (DESYNC) {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
} else {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_close_btree]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.INLINE_SIZE = 0
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfs3_file_desync(&lfs3, &file1) => 0;
|
|
}
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
if (DESYNC) {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
} else {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_close_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfs3_file_desync(&lfs3, &file1) => 0;
|
|
}
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
if (DESYNC) {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
} else {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_rm_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// remove the file
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(LOOKAHEAD && DESYNC)) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mv_src_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rename one file over another
|
|
lfs3_rename(&lfs3, "spider", "tarantula") => 0;
|
|
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mv_dst_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rename one file over another
|
|
lfs3_rename(&lfs3, "tarantula", "spider") => 0;
|
|
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_split]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create enough files for mroot to split
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_split_bshrub_l]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse a data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mroot to split
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_split_bshrub_r]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "zodarion",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// and another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mroot to split
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "zodarion", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_extend]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_extend_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse a data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_relocate]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
lfs3_block_t orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// it's a bit unclear if clobbered mroot chain traversals should
|
|
// still traverse inlined mroots, so if this breaks in the future
|
|
// I wouldn't worry too much about it
|
|
//
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_relocate_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroots
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse a data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
lfs3_block_t orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_split]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_split_bshrub_l]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_split_bshrub_r]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_extend]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// switch to early relocations after extending
|
|
lfs3_unmount(&lfs3) => 0;
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_recycles = 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_extend_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// switch to early relocations after extending
|
|
lfs3_unmount(&lfs3) => 0;
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_recycles = 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_relocate]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_relocate_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroots
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
# test traversals with mdir compaction
|
|
|
|
[cases.test_trvs_compact]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// write to our mdir until >gc_compact_thresh full
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
|
|
// hack, don't use the internals like this
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// we should be marked as uncompacted
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing and compacting
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// running another traversal should clear the uncompacted flag
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
while (true) {
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_compact_mrootchain]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// write to our mdir until mroot extends
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
|
|
uint8_t wbuf[SIZE];
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// now write to our mdir until mrootanchor >gc_compact_thresh full
|
|
while (true) {
|
|
// we need internals to check this
|
|
lfs3_mdir_t mrootanchor;
|
|
lfs3_mdir_fetch(&lfs3, &mrootanchor,
|
|
-1, LFS3_MPTR_MROOTANCHOR()) => 0;
|
|
if (lfs3_rbyd_eoff(&mrootanchor.r) > GC_COMPACT_THRESH) {
|
|
break;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// we should be marked as uncompacted
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing and compacting
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mrootanchor
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mrootanchor should have been compacted
|
|
lfs3_mdir_t mrootanchor;
|
|
lfs3_mdir_fetch(&lfs3, &mrootanchor,
|
|
-1, LFS3_MPTR_MROOTANCHOR()) => 0;
|
|
assert(lfs3_rbyd_eoff(&mrootanchor.r) <= GC_COMPACT_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// running another traversal should clear the uncompacted flag
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
while (true) {
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// mrootanchor should have been compacted
|
|
lfs3_mdir_fetch(&lfs3, &mrootanchor,
|
|
-1, LFS3_MPTR_MROOTANCHOR()) => 0;
|
|
assert(lfs3_rbyd_eoff(&mrootanchor.r) <= GC_COMPACT_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_compact_mroot_extend]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// write to our mdir until >gc_compact_thresh full
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
|
|
// hack, don't use the internals like this
|
|
uint8_t wbuf[SIZE];
|
|
while ((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// we should be marked as uncompacted
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing and compacting
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// it's a bit unclear if we should follow the mroot or stay on the
|
|
// mroot anchor during extends, so if this breaks in the future
|
|
// I wouldn't worry too much about it
|
|
//
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// running another traversal should clear the uncompacted flag
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
while (true) {
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the file
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_compact_mroot_split]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "jellyfish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create enough files to both compact and split
|
|
lfs3_size_t i = 0;
|
|
while (true) {
|
|
// we should not have split yet
|
|
assert(lfs3.mtree.r.weight == 0);
|
|
// we need internals to check this
|
|
lfs3_ssize_t estimate = lfs3_mdir_estimate___(&lfs3,
|
|
&file1.b.h.mdir, -1, -1,
|
|
NULL);
|
|
assert(estimate >= 0);
|
|
if ((file1.b.h.mdir.r.eoff & 0x7fffffff) > GC_COMPACT_THRESH
|
|
&& estimate > BLOCK_SIZE/2) {
|
|
break;
|
|
}
|
|
|
|
char name[256];
|
|
sprintf(name, "medusaaaaaaaaaaaaaaaa%03x", i);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// we should be marked as uncompacted
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing and compacting
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// should have split, traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// running another traversal should clear the uncompacted flag
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
while (true) {
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "jellyfish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_compact_mtree]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.COMPACTSET = 'range(0x8)'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "jellyfish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
lfs3_file_t file3;
|
|
lfs3_file_open(&lfs3, &file3, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file3, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file3) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "hydroid%03x", i);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "medusa%03x", i);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// write to each file until mdir >gc_compact_thresh full
|
|
if (COMPACTSET & 0x1) {
|
|
// hack, don't use the internals like this
|
|
while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
}
|
|
}
|
|
|
|
if (COMPACTSET & 0x2) {
|
|
// hack, don't use the internals like this
|
|
while ((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
}
|
|
}
|
|
|
|
if (COMPACTSET & 0x4) {
|
|
// hack, don't use the internals like this
|
|
while ((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file3) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file3, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file3) => 0;
|
|
}
|
|
}
|
|
|
|
// we should be marked as uncompacted
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing and compacting
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
if (COMPACTSET) {
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// running another traversal should clear the uncompacted flag
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
while (true) {
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_file_close(&lfs3, &file3) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "jellyfish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file3, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file3) => 0;
|
|
lfs3_file_read(&lfs3, &file3, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_file_close(&lfs3, &file3) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_compact_mtree_split]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
in = 'lfs3.c'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "jellyfish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
lfs3_file_t file3;
|
|
lfs3_file_open(&lfs3, &file3, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file3, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file3) => 0;
|
|
|
|
lfs3_file_t file4;
|
|
lfs3_file_open(&lfs3, &file4, "squid",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file4, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file4) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "hydroid%03x", i);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "polyp%03x", i);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// create enough files to both compact and split
|
|
i = 0;
|
|
orig = lfs3.mtree.r.weight;
|
|
while (true) {
|
|
// we should not have split yet
|
|
assert(lfs3.mtree.r.weight == orig);
|
|
// we need internals to check this
|
|
lfs3_ssize_t estimate = lfs3_mdir_estimate___(&lfs3,
|
|
&file2.b.h.mdir, -1, -1,
|
|
NULL);
|
|
assert(estimate >= 0);
|
|
if ((file2.b.h.mdir.r.eoff & 0x7fffffff) > GC_COMPACT_THRESH
|
|
&& estimate > BLOCK_SIZE/2) {
|
|
break;
|
|
}
|
|
|
|
char name[256];
|
|
sprintf(name, "medusaaaaaaaaaaaaaaaa%03x", i);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// we should be marked as uncompacted
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing and compacting
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// should have split, traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file4.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// running another traversal should clear the uncompacted flag
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
while (true) {
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file4.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_file_close(&lfs3, &file3) => 0;
|
|
lfs3_file_close(&lfs3, &file4) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "jellyfish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file3, "octopus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file4, "squid", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file3) => 0;
|
|
lfs3_file_read(&lfs3, &file3, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file4) => 0;
|
|
lfs3_file_read(&lfs3, &file4, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_file_close(&lfs3, &file3) => 0;
|
|
lfs3_file_close(&lfs3, &file4) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
# test traversals with mkconsistent
|
|
|
|
[cases.test_trvs_mkconsistent]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_LOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS > 3) {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mkconsistent_conflict]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// we should not be marked as inconsistent
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// keep traversing
|
|
if (ORPHANS <= 3) {
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
} else {
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should be able to clean up grms
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
// if we introduce actual orphans, me _must not_ clear the orphan flag
|
|
if (ORPHANS >= 3) {
|
|
assert(lfs3.flags & LFS3_I_MKCONSISTENT);
|
|
}
|
|
|
|
// if we introduced actual orphans, we _must_ be marked as inconsistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS >= 3) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mkconsistent_btree]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.INLINE_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mkconsistent_btree_uncreat]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.INLINE_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mkconsistent_bshrub]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mkconsistent_bshrub_uncreat]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_LOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD || ORPHANS > 0) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACT
|
|
// note ckdata implies ckmeta
|
|
| (((!CKMETA && !CKDATA) || ORPHANS > 0) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA || ORPHANS > 0) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mkconsistent_compact]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// write to our mdirs until >gc_compact_thresh full
|
|
//
|
|
// hack, don't use the internals like this
|
|
while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
}
|
|
|
|
while ((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent and uncompacted
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_LOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS > 3) {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// we should be marked as consistent, but because we mutated, we're
|
|
// still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// running another traversal should clear the uncompacted flag
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
while (true) {
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!LOOKAHEAD) ? LFS3_I_LOOKAHEAD : 0)
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mkconsistent_compact_conflict]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FILE_CACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// write to our mdirs until >gc_compact_thresh full
|
|
//
|
|
// hack, don't use the internals like this
|
|
while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
}
|
|
|
|
while ((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
}
|
|
|
|
// we should not be marked as inconsistent
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_LOOKAHEAD
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try traversing with mkconsistent
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| LFS3_T_MKCONSISTENT
|
|
| LFS3_T_COMPACT
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// keep traversing
|
|
if (ORPHANS <= 3) {
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
} else {
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should be able to clean up grms
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
// if we introduce actual orphans, me _must not_ clear the orphan flag
|
|
if (ORPHANS >= 3) {
|
|
assert(lfs3.flags & LFS3_I_MKCONSISTENT);
|
|
}
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
|
|
|
// if we introduced actual orphans, we _must_ be marked as inconsistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS >= 3) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_LOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REPOPGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACT
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check we can still read the files
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_open(&lfs3, &file2, "octopus", LFS3_O_RDONLY) => 0;
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file1, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
|
|
lfs3_file_rewind(&lfs3, &file2) => 0;
|
|
lfs3_file_read(&lfs3, &file2, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
}
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# many/fuzz tests mixed with traversals
|
|
#
|
|
# these should hopefully test a bunch of messy traversal state
|
|
#
|
|
|
|
[cases.test_trvs_spam_dir_many]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
if = 'GBMAP || !REPOPGBMAP'
|
|
code = '''
|
|
// test creating directories
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// open a traversal
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// make this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
int err = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || (TEST_PLS && err == LFS3_ERR_EXIST));
|
|
|
|
// step the traversal
|
|
for (lfs3_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs3_tinfo tinfo;
|
|
err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs3.grm_p[0] == 0);
|
|
|
|
// check that our mkdir worked
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_open(&lfs3, &dir, name) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_spam_dir_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
defines.OPS = '2*N'
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = 'GBMAP || !REPOPGBMAP'
|
|
code = '''
|
|
// test fuzz with dirs
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
// open a traversal
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_size_t i = 0; i < OPS; i++) {
|
|
// choose a pseudo-random op, either mkdir, remove, or rename
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number, truncate to 3 hexadecimals
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
// insert into our sim
|
|
for (lfs3_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// do nothing
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs3_size_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a directory here
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
int err = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_EXIST);
|
|
|
|
} else if (op == 1) {
|
|
// choose a pseudo-random entry to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
sim_size -= 1;
|
|
|
|
// remove this directory
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
} else {
|
|
// choose a pseudo-random entry to rename, and a pseudo-random
|
|
// number to rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// already seen and not a noop?
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// just delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
sim_size -= 1;
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
sim[k] = y;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this directory
|
|
char old_name[256];
|
|
sprintf(old_name, "dir%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "dir%03x", y);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs3_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs3.grm_p[0] == 0);
|
|
|
|
// test that our directories match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
char name2[256];
|
|
sprintf(name2, "dir%03x", sim[j]);
|
|
assert(strcmp(info.name, name2) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_spam_file_many]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 32',
|
|
'GBMAP || !REPOPGBMAP',
|
|
]
|
|
code = '''
|
|
// test creating files
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// open a traversal
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// step the traversal
|
|
for (lfs3_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that our writes worked
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_spam_file_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 16',
|
|
'GBMAP || !REPOPGBMAP',
|
|
]
|
|
code = '''
|
|
// test fuzz with files
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
// open a traversal
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_size_t i = 0; i < OPS; i++) {
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs3_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%03x", y);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs3_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// check the file contents
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_spam_fwrite_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.OPS = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# chunk is more an upper limit here
|
|
defines.CHUNK = [32, 8, 1]
|
|
# INIT=0 => no init
|
|
# INIT=1 => fill with data
|
|
# INIT=2 => truncate to size
|
|
defines.INIT = [0, 1, 2]
|
|
defines.SYNC = [false, true]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'CHUNK <= SIZE',
|
|
# this just saves testing time
|
|
'SIZE <= 4*1024*FRAGMENT_SIZE',
|
|
'GBMAP || !REPOPGBMAP',
|
|
]
|
|
code = '''
|
|
// test with complex file writes
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
// simulate our file in ram
|
|
uint8_t sim[SIZE];
|
|
lfs3_off_t size;
|
|
uint32_t prng = SEED;
|
|
if (INIT == 0) {
|
|
memset(sim, 0, SIZE);
|
|
size = 0;
|
|
} else if (INIT == 1) {
|
|
for (lfs3_size_t i = 0; i < SIZE; i++) {
|
|
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, sim, SIZE) => SIZE;
|
|
size = SIZE;
|
|
} else {
|
|
memset(sim, 0, SIZE);
|
|
lfs3_file_truncate(&lfs3, &file, SIZE) => 0;
|
|
size = SIZE;
|
|
}
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// open a traversal
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < OPS; i++) {
|
|
// choose a random location
|
|
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
// and a random size, up to the chunk size
|
|
lfs3_size_t chunk = lfs3_min(
|
|
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
|
|
SIZE - off);
|
|
|
|
// update sim
|
|
for (lfs3_size_t j = 0; j < chunk; j++) {
|
|
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
size = lfs3_max(size, off+chunk);
|
|
|
|
// update file
|
|
lfs3_file_seek(&lfs3, &file, off, LFS3_SEEK_SET) => off;
|
|
lfs3_file_write(&lfs3, &file, &sim[off], chunk) => chunk;
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs3_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check our file with stat
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == size);
|
|
|
|
// and with dir read
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == size);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// try reading our file
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfs3_file_size(&lfs3, &file) => size;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfs3_file_read(&lfs3, &file, rbuf, 2*SIZE) => size;
|
|
// does our file match our simulation?
|
|
assert(memcmp(rbuf, sim, size) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_spam_uz_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 16',
|
|
'GBMAP || !REPOPGBMAP',
|
|
]
|
|
code = '''
|
|
// test with uncreats, zombies, etc
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isstickys = malloc(N*sizeof(bool));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs3_size_t x;
|
|
bool sticky;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfs3_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs3_size_t sim_file_count = 0;
|
|
|
|
// open a traversal
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 5;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool exist = false;
|
|
uint32_t wprng = 0;
|
|
bool sticky = true;
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
exist = true;
|
|
wprng = sim_prngs[j];
|
|
sticky = sim_isstickys[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (!exist) {
|
|
wprng = TEST_PRNG(&prng);
|
|
sticky = true;
|
|
}
|
|
|
|
lfs3_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_file_open(&lfs3, &sim_files[j]->file, name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (!exist || sticky) {
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE)
|
|
=> SIZE;
|
|
}
|
|
|
|
// open in our sim
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->sticky = sticky;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// insert into our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// write to the file
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0;
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// update in our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
// no longer sticky
|
|
sim_isstickys[k] = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
// new prng
|
|
sim_files[k]->prng = wprng;
|
|
// no longer sticky
|
|
sim_files[k]->sticky = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
bool sticky = sim_files[j]->sticky;
|
|
bool zombie = sim_files[j]->zombie;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// update our sim
|
|
if (sticky && !zombie) {
|
|
// orphaned?
|
|
bool orphan = true;
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
orphan = false;
|
|
}
|
|
}
|
|
|
|
// if we were never synced, delete from sim
|
|
if (orphan) {
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
memmove(&sim[k], &sim[k+1],
|
|
(sim_size-(k+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k], &sim_prngs[k+1],
|
|
(sim_size-(k+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool sticky = sim_isstickys[j];
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
|
|
// update our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng/sticky
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs3_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
if (sim_isstickys[j]) {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0;
|
|
} else {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
free(sim_prngs);
|
|
free(sim_isstickys);
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_spam_uzd_fuzz]
|
|
# traverse steps between each op
|
|
defines.STEPS = [1, 2, 4, 8, 16, 32, 64, 128]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.LOOKAHEAD = [false, true]
|
|
defines.REPOPGBMAP = [false, true]
|
|
defines.COMPACT = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compact thresh to minimum
|
|
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 16',
|
|
'GBMAP || !REPOPGBMAP',
|
|
]
|
|
code = '''
|
|
// test with uncreats, zombies, dirs, etc
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isstickys = malloc(N*sizeof(bool));
|
|
bool *sim_isdirs = malloc(N*sizeof(bool));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs3_size_t x;
|
|
bool sticky;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfs3_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs3_size_t sim_file_count = 0;
|
|
|
|
// open a traversal
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((MKCONSISTENT) ? LFS3_T_MKCONSISTENT : 0)
|
|
| ((LOOKAHEAD) ? LFS3_T_LOOKAHEAD : 0)
|
|
| ((REPOPGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REPOPGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACT) ? LFS3_T_COMPACT : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 8;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool exist = true;
|
|
uint32_t wprng = 0;
|
|
bool sticky = true;
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
exist = true;
|
|
wprng = sim_prngs[j];
|
|
sticky = sim_isstickys[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (!exist) {
|
|
wprng = TEST_PRNG(&prng);
|
|
sticky = true;
|
|
}
|
|
|
|
lfs3_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_file_open(&lfs3, &sim_files[j]->file, name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (!exist || sticky) {
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE)
|
|
=> SIZE;
|
|
}
|
|
|
|
// open in our sim
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->sticky = sticky;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// insert into our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// write to the file
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0;
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// update in our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
// no longer sticky
|
|
sim_isstickys[k] = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
// new prng
|
|
sim_files[k]->prng = wprng;
|
|
// no longer sticky
|
|
sim_files[k]->sticky = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
lfs3_size_t sticky = sim_files[j]->sticky;
|
|
lfs3_size_t zombie = sim_files[j]->zombie;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// update our sim
|
|
if (sticky && !zombie) {
|
|
// orphaned?
|
|
bool orphan = true;
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
orphan = false;
|
|
}
|
|
}
|
|
|
|
// if we were never synced, delete from sim
|
|
if (orphan) {
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
memmove(&sim[k], &sim[k+1],
|
|
(sim_size-(k+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k], &sim_prngs[k+1],
|
|
(sim_size-(k+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[k], &sim_isdirs[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool sticky = sim_isstickys[j];
|
|
bool dir = sim_isdirs[j];
|
|
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// type mismatch?
|
|
if (sim_isdirs[k] != dir) {
|
|
goto nonsense;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
|
|
// update our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng/sticky/dir
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = dir;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = dir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// toss a directory into the mix
|
|
} else if (op == 5) {
|
|
// choose a pseudo-random number
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
goto nonsense;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// make the directory
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
|
|
// insert into our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = 0;
|
|
sim_isdirs[k] = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// step the traversal
|
|
for (lfs3_size_t s = 0; s < STEPS; s++) {
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
// restart traversal
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim_isdirs[j]) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY)
|
|
=> LFS3_ERR_ISDIR;
|
|
|
|
} else {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
if (sim_isstickys[j]) {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0;
|
|
} else {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
free(sim_prngs);
|
|
free(sim_isstickys);
|
|
free(sim_isdirs);
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|