4010afeafd
With the relaxation of traversal behavior under mutation, I think it
makes sense to bring back LFS3_T_EXCL. If only to allow traversals to
gaurantee termination under mutation. Now that traversals no longer
guarantee forward progress, it's possible to get stuck looping
indefinitely if the filesystem is constantly being mutated.
Non-excl traversals are probably still useful for GC work and debugging
threads, but LFS3_T_EXCL now allows traversals to terminate immediately
with LFS3_ERR_BUSY at the first sign of unrelated filesystem mutation:
LFS3_T_EXCL 0x00000008 Error if filesystem modified
Internally, we already track unrelated mutation to avoid corrupt state
(LFS3_t_DIRTY), so this is a very low-cost feature:
code stack ctx
before: 35944 2280 660
after: 35964 (+0.1%) 2280 (+0.0%) 660 (+0.0%)
code stack ctx
gbmap before: 38916 2296 772
gbmap after: 38940 (+0.1%) 2296 (+0.0%) 772 (+0.0%)
code stack ctx
gc before: 36016 2280 768
gc after: 36036 (+0.1%) 2280 (+0.0%) 768 (+0.0%)
10831 lines
364 KiB
TOML
10831 lines
364 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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = 'GBMAP || !REGBMAP'
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# can we rewind?
|
|
[cases.test_trvs_rewind]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.COMPACTMETA = [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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we don't get extra anything after end of traversal
|
|
[cases.test_trvs_idempotent]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = 'GBMAP || !REGBMAP'
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# some simple traversal tests with clobbering
|
|
[cases.test_trvs_clobber_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that we can read our directories after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_clobber_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_clobber_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_rewind(&lfs3, &files[i]) => 0;
|
|
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// and everything is fine after saving the files
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# a bit more aggressive rewind tests
|
|
[cases.test_trvs_rewind_clobber_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
lfs3_block_t r = 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that we can read our directories after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_rewind_clobber_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
lfs3_block_t r = 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_rewind_clobber_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find all blocks in use
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDONLY
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
lfs3_block_t r = 0;
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
if (i == r) {
|
|
lfs3_trv_rewind(&lfs3, &trv) => 0;
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
r += 1;
|
|
i = -1;
|
|
continue;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
printf("traversal: btype %d block 0x%x\n",
|
|
tinfo.btype,
|
|
tinfo.block);
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR
|
|
|| tinfo.btype == LFS3_BTYPE_BTREE
|
|
|| tinfo.btype == LFS3_BTYPE_DATA);
|
|
// keep track of seen blocks
|
|
seen[tinfo.block / 8] |= 1 << (tinfo.block % 8);
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// clobber every other block
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs3_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// then check that reading our files still works after clobbering
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_rewind(&lfs3, &files[i]) => 0;
|
|
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// and everything is fine after saving the files
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered mdir
|
|
[cases.test_trvs_ckmdir_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (k == i+1) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckmdir_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (k == i+1) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckmdir_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i += 2) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_MDIR) {
|
|
if (k == i || k == i+1) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
if (k == i+1) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_desync(&lfs3, &files[i]) => 0;
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered btree
|
|
[cases.test_trvs_ckbtree_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_BTREE) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckbtree_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_BTREE) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckbtree_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_BTREE) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKMETA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_desync(&lfs3, &files[i]) => 0;
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# check that we can detect every clobbered data block
|
|
[cases.test_trvs_ckdata_dirs]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_mkdir(&lfs3, name) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_DATA) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckdata_files]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_DATA) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
[cases.test_trvs_ckdata_files_opened]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
for (lfs3_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*BLOCK_COUNT);
|
|
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// create this many files
|
|
lfs3_file_t files[N];
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "file%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &files[i], name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// traverse to find blocks
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0;
|
|
lfs3_block_t k = 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
goto done;
|
|
}
|
|
|
|
if (tinfo.btype == LFS3_BTYPE_DATA) {
|
|
if (k == i) {
|
|
// clobber this block
|
|
printf("clobbering 0x%x\n", tinfo.block);
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
CFG->erase(CFG, tinfo.block) => 0;
|
|
CFG->prog(CFG, tinfo.block, 0,
|
|
clobber_buf, BLOCK_SIZE) => 0;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto clobbered;
|
|
}
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
clobbered:;
|
|
// traverse again, we should detect the clobbered metadata
|
|
lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY | LFS3_T_CKDATA) => 0;
|
|
for (lfs3_block_t j = 0;; j++) {
|
|
assert(j < 2*BLOCK_COUNT);
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err || err == LFS3_ERR_CORRUPT);
|
|
// found the clobbered metadata?
|
|
if (err == LFS3_ERR_CORRUPT) {
|
|
break;
|
|
}
|
|
}
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
lfs3_file_desync(&lfs3, &files[i]) => 0;
|
|
lfs3_file_close(&lfs3, &files[i]) => 0;
|
|
}
|
|
lfs3_unmount(&lfs3) => 0;
|
|
}
|
|
done:;
|
|
'''
|
|
|
|
|
|
|
|
# test that in general fsinfo flags work
|
|
[cases.test_trvs_flags]
|
|
defines.MKCONSISTENT = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
if = 'GBMAP || !REGBMAP'
|
|
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_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| 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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// check flags after
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!MKCONSISTENT) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((!COMPACTMETA) ? LFS3_I_COMPACTMETA : 0)
|
|
// note ckdata implies ckmeta
|
|
| ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we detect filesystem mutation during traversal
|
|
[cases.test_trvs_mutation]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
// terminate early?
|
|
if (EXCL && WHEN == 0) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
if (WHEN != 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
}
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 2) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# test that we don't get extra anything after end of traversal
|
|
[cases.test_trvs_mutation_idempotent]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// mutate
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
} else {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
}
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// try another mutation just for good measure
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
} else {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
}
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mkdir]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_mkdir(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
// terminate early?
|
|
if (EXCL && WHEN == 0) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_mkdir(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
if (WHEN != 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
}
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_mkdir(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 2) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_rm]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// make a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
// terminate early?
|
|
if (EXCL && WHEN == 0) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
if (WHEN != 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
}
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 2) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mv]
|
|
defines.WHEN = [0, 1, 2]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// make a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
if (WHEN == 0) {
|
|
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
struct lfs3_tinfo tinfo;
|
|
// terminate early?
|
|
if (EXCL && WHEN == 0) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
if (WHEN == 1) {
|
|
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
if (WHEN != 1) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
|
|
if (WHEN == 2) {
|
|
lfs3_rename(&lfs3, "spider", "scorpion") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL && WHEN == 2) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
if (WHEN == 2) {
|
|
// traverse gbmap again
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
# some more complex mutation tests
|
|
[cases.test_trvs_mutation_fwrite]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// mutating traversals may never terminate, so stop at some point
|
|
for (lfs3_size_t i = 0; i < 4*BLOCK_COUNT; i++) {
|
|
// rewrite the file every step of the traversal
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// step traversal
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err
|
|
|| err == LFS3_ERR_NOENT
|
|
|| (EXCL && err == LFS3_ERR_BUSY));
|
|
if (err == LFS3_ERR_NOENT
|
|
|| err == LFS3_ERR_BUSY) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP && SIZE >= BLOCK_SIZE/4)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_fwrite_opened]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = [
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'8*BLOCK_SIZE',
|
|
]
|
|
defines.SYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create a file
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file) => 0;
|
|
if (SYNC) {
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// mutating traversals may never terminate, so stop at some point
|
|
for (lfs3_size_t i = 0; i < 4*BLOCK_COUNT; i++) {
|
|
// rewrite the file every step of the traversal
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
if (SYNC) {
|
|
lfs3_file_sync(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// step traversal
|
|
struct lfs3_tinfo tinfo;
|
|
int err = lfs3_trv_read(&lfs3, &trv, &tinfo);
|
|
assert(!err
|
|
|| err == LFS3_ERR_NOENT
|
|
|| (EXCL && err == LFS3_ERR_BUSY));
|
|
if (err == LFS3_ERR_NOENT
|
|
|| err == LFS3_ERR_BUSY) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
// ok we might've updated lookahead if write stayed in cache
|
|
((!RELOOKAHEAD || SIZE > FCACHE_SIZE || SYNC)
|
|
? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP && SIZE >= BLOCK_SIZE/4)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// and maybe ckmeta/ckdata, note ckdata implies ckmeta
|
|
| (((!CKMETA && !CKDATA) || SIZE > FCACHE_SIZE || SYNC)
|
|
? LFS3_I_CKMETA : 0)
|
|
| ((!CKDATA || SIZE > FCACHE_SIZE || SYNC)
|
|
? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_rewind(&lfs3, &file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// and after close?
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
# test specific cases where we need to clobber traversals
|
|
#
|
|
# these assume quite a bit more and may be a bit fragile...
|
|
#
|
|
[cases.test_trvs_mutation_file_bsprout]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite the file
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY
|
|
| ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_file_btree]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.SHRUB_SIZE = 0
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY
|
|
| ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_file_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.TRUNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY
|
|
| ((TRUNC) ? LFS3_O_TRUNC : 0)) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_uncreat_bsprout]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite the file
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_uncreat_btree]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.SHRUB_SIZE = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_uncreat_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite the file
|
|
lfs3_file_rewind(&lfs3, &file1) => 0;
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_close_bsprout]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfs3_file_desync(&lfs3, &file1) => 0;
|
|
}
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
|
|
// if not desync, traversal rewinds to mdir
|
|
if (!DESYNC) {
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap again
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
}
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(RELOOKAHEAD && DESYNC)) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
if (DESYNC) {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
} else {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_close_btree]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.SHRUB_SIZE = 0
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfs3_file_desync(&lfs3, &file1) => 0;
|
|
}
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
|
|
// if not desync, traversal rewinds to mdir
|
|
if (!DESYNC) {
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
}
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(RELOOKAHEAD && DESYNC)) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
if (DESYNC) {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
} else {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_close_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
defines.DESYNC = [false, true]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// close the file
|
|
if (DESYNC) {
|
|
lfs3_file_desync(&lfs3, &file1) => 0;
|
|
}
|
|
lfs3_file_close(&lfs3, &file1) => 0;
|
|
|
|
// if not desync, traversal rewinds to mdir
|
|
if (!DESYNC) {
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
}
|
|
// traverse another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(RELOOKAHEAD && DESYNC)) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
lfs3_file_close(&lfs3, &file2) => 0;
|
|
|
|
// check the file contents
|
|
lfs3_file_t file;
|
|
if (DESYNC) {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
} else {
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_rm_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// remove the file
|
|
lfs3_remove(&lfs3, "spider") => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/etc, unless we're desynced
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((DESYNC) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!(RELOOKAHEAD && DESYNC)) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// note ckdata implies ckmeta
|
|
| ((!(CKMETA && DESYNC) && !(CKDATA && DESYNC))
|
|
? LFS3_I_CKMETA
|
|
: 0)
|
|
| ((!(CKDATA && DESYNC)) ? LFS3_I_CKDATA : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mv_src_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rename one file over another
|
|
lfs3_rename(&lfs3, "spider", "tarantula") => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mv_dst_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rename one file over another
|
|
lfs3_rename(&lfs3, "tarantula", "spider") => 0;
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_split]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create enough files for mroot to split
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_split_bshrub_l]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse a data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mroot to split
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse an mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "tarantula", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_split_bshrub_r]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create three files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "zodarion",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// and another data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mroot to split
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "uloborus%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "zodarion", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_extend]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_extend_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse a data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_relocate]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
lfs3_block_t orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// it's a bit unclear if clobbered mroot chain traversals should
|
|
// still traverse inlined mroots, so if this breaks in the future
|
|
// I wouldn't worry too much about it
|
|
//
|
|
// traverse an mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mroot_relocate_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroots
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse a data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
lfs3_block_t orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "uloborus") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_split]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_split_bshrub_l]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_split_bshrub_r]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// create enough files for mdir to split again
|
|
i = 0;
|
|
orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "vulsor%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_extend]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// switch to early relocations after extending
|
|
lfs3_unmount(&lfs3) => 0;
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_recycles = 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_extend_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// switch to early relocations after extending
|
|
lfs3_unmount(&lfs3) => 0;
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_recycles = 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_relocate]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse 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 mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| LFS3_I_CKMETA
|
|
| LFS3_I_CKDATA
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0)));
|
|
|
|
// check the file contents
|
|
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf4, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_trvs_mutation_mtree_relocate_bshrub]
|
|
defines.EXCL = [false, true]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = '2*BLOCK_SIZE'
|
|
# force early relocations
|
|
defines.BLOCK_RECYCLES = 0
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create four files
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "spider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "uloborus",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "wolfspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf3[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf3, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_file_open(&lfs3, &file, "yellowcrabspider",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf4[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file, wbuf4, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// rewrite enough files for mroot to extend
|
|
while (lfs3.mroot.r.blocks[0] == 0
|
|
|| lfs3.mroot.r.blocks[0] == 1) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// create enough files for mroot to split twice
|
|
lfs3_size_t i = 0;
|
|
while (lfs3.mtree.r.weight == 0) {
|
|
char name[256];
|
|
sprintf(name, "tarantula%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
i = 0;
|
|
lfs3_size_t orig = lfs3.mtree.r.weight;
|
|
while (lfs3.mtree.r.weight == orig) {
|
|
char name[256];
|
|
sprintf(name, "xnotata%03x", i);
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
i += 1;
|
|
}
|
|
|
|
// try traversing
|
|
lfs3_trv_t trv;
|
|
lfs3_trv_open(&lfs3, &trv,
|
|
LFS3_T_RDWR
|
|
| ((EXCL) ? LFS3_T_EXCL : 0)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
// traverse mroots
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse one data block
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
|
|
// rewrite enough files for mroot to relocate
|
|
orig = lfs3.mroot.r.blocks[0];
|
|
while (lfs3.mroot.r.blocks[0] == orig
|
|
|| lfs3.mroot.r.blocks[0] == orig) {
|
|
lfs3_file_open(&lfs3, &file, "vulsor",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
lfs3_remove(&lfs3, "vulsor") => 0;
|
|
}
|
|
|
|
// terminate early?
|
|
if (EXCL) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_BUSY;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
goto done;
|
|
}
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse two data blocks
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_DATA);
|
|
// we should be at end of traversal now
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
done:;
|
|
// we should _not_ update lookahead/compact/ckmeta/ckdata
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_compactmeta]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_compactmeta_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 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_compactmeta_mrootchain]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_compactmeta_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mrootanchor
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mrootanchor should have been compacted
|
|
lfs3_mdir_t mrootanchor;
|
|
lfs3_mdir_fetch(&lfs3, &mrootanchor,
|
|
-1, LFS3_MPTR_MROOTANCHOR()) => 0;
|
|
assert(lfs3_rbyd_eoff(&mrootanchor.r) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -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_compactmeta_mroot_extend]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_compactmeta_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// it's a bit unclear if we should follow the mroot or stay on the
|
|
// mroot anchor during extends, so if this breaks in the future
|
|
// I wouldn't worry too much about it
|
|
//
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdir should have been compacted
|
|
assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -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_compactmeta_mroot_split]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// should have split, traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -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_compactmeta_mtree]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_compactmeta_thresh full
|
|
if (COMPACTSET & 0x1) {
|
|
// hack, don't use the internals like this
|
|
while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_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_COMPACTMETA_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
if (COMPACTSET) {
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -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_compactmeta_mtree_split]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// should have split, traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse mdir
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file4.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// but because we mutated, we're still marked as uncompacted
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file3.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file4.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -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.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD || ORPHANS > 0) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// 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.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// we should not be marked as inconsistent
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
LFS3_I_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| 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
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// keep traversing
|
|
if (ORPHANS <= 3) {
|
|
if (ORPHANS == 0) {
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
} else {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should be able to clean up grms
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
// if we introduce actual orphans, me _must not_ clear the orphan flag
|
|
if (ORPHANS >= 3) {
|
|
assert(lfs3.flags & LFS3_I_MKCONSISTENT);
|
|
}
|
|
|
|
// if we introduced actual orphans, we _must_ be marked as inconsistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS >= 3) ? LFS3_I_MKCONSISTENT : 0)
|
|
| ((!RELOOKAHEAD || ORPHANS > 0) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// 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.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.SHRUB_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD || ORPHANS > 0) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// 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.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# limit files to very simple btrees
|
|
defines.SHRUB_SIZE = 0
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = '2*FRAGMENT_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse btree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD || ORPHANS > 0) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// 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.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.SHRUB_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD || ORPHANS > 0) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// 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.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
# this configuration should create a 2-layer bshrub, which may be
|
|
# a bit delicate
|
|
defines.SHRUB_SIZE = 'BLOCK_SIZE/4'
|
|
defines.CRYSTAL_THRESH = -1
|
|
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
|
defines.SIZE = 'BLOCK_SIZE'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
code = '''
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create two files
|
|
lfs3_file_t file1;
|
|
lfs3_file_open(&lfs3, &file1, "cuttlefish",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf1[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file1, wbuf1, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file1) => 0;
|
|
|
|
lfs3_file_t file2;
|
|
lfs3_file_open(&lfs3, &file2, "octopus",
|
|
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf2[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &file2, wbuf2, SIZE) => SIZE;
|
|
lfs3_file_flush(&lfs3, &file2) => 0;
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// we should be marked as inconsistent now
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((ORPHANS > 0) ? LFS3_I_MKCONSISTENT : 0)
|
|
| LFS3_I_RELOOKAHEAD
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
// traverse bshrub
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// and we should be marked as consistent
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD || ORPHANS > 0) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1) : 0)
|
|
| LFS3_I_COMPACTMETA
|
|
// 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_compactmeta]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_compactmeta_thresh full
|
|
//
|
|
// hack, don't use the internals like this
|
|
while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
if (ORPHANS <= 3) {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
} else {
|
|
// traverse mtree
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
|
|
|
|
// which means there shouldn't be that many files left
|
|
assert(lfs3.mtree.r.weight <= (2 << lfs3.mbits));
|
|
assert(file1.b.h.mdir.r.weight <= 3);
|
|
assert(file2.b.h.mdir.r.weight <= 3);
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_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_RELOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
|
|
// uncompacted flag should have been cleared
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.flags == (
|
|
((!RELOOKAHEAD) ? LFS3_I_RELOOKAHEAD : 0)
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -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_compactmeta_conflict]
|
|
defines.RELOOKAHEAD = [false, true]
|
|
defines.CKMETA = [false, true]
|
|
defines.CKDATA = [false, true]
|
|
defines.SIZE = 'FCACHE_SIZE/2'
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.ORPHANS = [0, 1, 2, 3, 100]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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_compactmeta_thresh full
|
|
//
|
|
// hack, don't use the internals like this
|
|
while ((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_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_COMPACTMETA_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_RELOOKAHEAD
|
|
| LFS3_I_COMPACTMETA
|
|
| 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_COMPACTMETA
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
// traverse mroot
|
|
struct lfs3_tinfo tinfo;
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfs3_file_t orphans[ORPHANS];
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
char name[256];
|
|
sprintf(name, "jellyfish%03x", i);
|
|
lfs3_file_open(&lfs3, &orphans[i], name,
|
|
LFS3_O_WRONLY
|
|
| LFS3_O_CREAT
|
|
| LFS3_O_EXCL
|
|
| LFS3_O_DESYNC) => 0;
|
|
}
|
|
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
|
|
lfs3_file_close(&lfs3, &orphans[i]) => 0;
|
|
}
|
|
|
|
// keep traversing
|
|
if (ORPHANS <= 3) {
|
|
if (ORPHANS == 0) {
|
|
// traverse mroot
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
assert(tinfo.block == 0 || tinfo.block == 1);
|
|
}
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
} else {
|
|
// traverse gbmap
|
|
if (GBMAP) {
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_BTREE);
|
|
assert(tinfo.block == 2);
|
|
}
|
|
// traverse mdirs
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => 0;
|
|
assert(tinfo.btype == LFS3_BTYPE_MDIR);
|
|
}
|
|
lfs3_trv_read(&lfs3, &trv, &tinfo) => LFS3_ERR_NOENT;
|
|
lfs3_trv_close(&lfs3, &trv) => 0;
|
|
|
|
// we should be able to clean up grms
|
|
assert(lfs3.grm.queue[0] == 0);
|
|
assert(lfs3.grm.queue[1] == 0);
|
|
// if we introduce actual orphans, me _must not_ clear the orphan flag
|
|
if (ORPHANS >= 3) {
|
|
assert(lfs3.flags & LFS3_I_MKCONSISTENT);
|
|
}
|
|
|
|
// mdirs should have been compacted
|
|
assert((file1.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_THRESH);
|
|
assert((file2.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACTMETA_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_RELOOKAHEAD
|
|
| ((GBMAP && ORPHANS >= 100)
|
|
? LFS3_IFDEF_GBMAP(LFS3_I_REGBMAP, -1)
|
|
: 0)
|
|
| LFS3_I_COMPACTMETA
|
|
| 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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
if = 'GBMAP || !REGBMAP'
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_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 || !REGBMAP'
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 32',
|
|
'GBMAP || !REGBMAP',
|
|
]
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_THRESH = 'BLOCK_SIZE/2'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 16',
|
|
'GBMAP || !REGBMAP',
|
|
]
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_THRESH = 'BLOCK_SIZE/2'
|
|
defines.OPS = 20
|
|
defines.SIZE = [
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# chunk is more an upper limit here
|
|
defines.CHUNK = [32, 8, 1]
|
|
# INIT=0 => no init
|
|
# INIT=1 => fill with data
|
|
# INIT=2 => truncate to size
|
|
defines.INIT = [0, 1, 2]
|
|
defines.SYNC = [false, true]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'CHUNK <= SIZE',
|
|
# this just saves testing time
|
|
'SIZE <= 4*1024*FRAGMENT_SIZE',
|
|
'GBMAP || !REGBMAP',
|
|
]
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_THRESH = 'BLOCK_SIZE/2'
|
|
# you probably need to flush if you expect errors
|
|
defines.FLUSH = false
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 16',
|
|
'GBMAP || !REGBMAP',
|
|
]
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 5;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool exist = false;
|
|
uint32_t wprng = 0;
|
|
bool sticky = true;
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
exist = true;
|
|
wprng = sim_prngs[j];
|
|
sticky = sim_isstickys[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (!exist) {
|
|
wprng = TEST_PRNG(&prng);
|
|
sticky = true;
|
|
}
|
|
|
|
lfs3_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_file_open(&lfs3, &sim_files[j]->file, name,
|
|
LFS3_O_RDWR
|
|
| LFS3_O_CREAT
|
|
| ((FLUSH) ? LFS3_O_FLUSH : 0)) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (!exist || sticky) {
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE)
|
|
=> SIZE;
|
|
}
|
|
|
|
// open in our sim
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->sticky = sticky;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// insert into our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// write to the file
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0;
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// update in our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
// no longer sticky
|
|
sim_isstickys[k] = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
// new prng
|
|
sim_files[k]->prng = wprng;
|
|
// no longer sticky
|
|
sim_files[k]->sticky = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
bool sticky = sim_files[j]->sticky;
|
|
bool zombie = sim_files[j]->zombie;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// update our sim
|
|
if (sticky && !zombie) {
|
|
// orphaned?
|
|
bool orphan = true;
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
orphan = false;
|
|
}
|
|
}
|
|
|
|
// if we were never synced, delete from sim
|
|
if (orphan) {
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
memmove(&sim[k], &sim[k+1],
|
|
(sim_size-(k+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k], &sim_prngs[k+1],
|
|
(sim_size-(k+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool sticky = sim_isstickys[j];
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
|
|
// update our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng/sticky
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-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.RELOOKAHEAD = [false, true]
|
|
defines.REGBMAP = [false, true]
|
|
defines.COMPACTMETA = [false, true]
|
|
defines.CKMETA = [true]
|
|
defines.CKDATA = [true]
|
|
# set compactmeta thresh to minimum
|
|
defines.GC_COMPACTMETA_THRESH = 'BLOCK_SIZE/2'
|
|
# you probably need to flush if you expect errors
|
|
defines.FLUSH = false
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FCACHE_SIZE/2',
|
|
'2*FCACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 16',
|
|
'GBMAP || !REGBMAP',
|
|
]
|
|
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)
|
|
| ((RELOOKAHEAD) ? LFS3_T_RELOOKAHEAD : 0)
|
|
| ((REGBMAP)
|
|
? LFS3_IFDEF_GBMAP(LFS3_T_REGBMAP, -1)
|
|
: 0)
|
|
| ((COMPACTMETA) ? LFS3_T_COMPACTMETA : 0)
|
|
| ((CKMETA) ? LFS3_T_CKMETA : 0)
|
|
| ((CKDATA) ? LFS3_T_CKDATA : 0)) => 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 8;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool exist = true;
|
|
uint32_t wprng = 0;
|
|
bool sticky = true;
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
exist = true;
|
|
wprng = sim_prngs[j];
|
|
sticky = sim_isstickys[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (!exist) {
|
|
wprng = TEST_PRNG(&prng);
|
|
sticky = true;
|
|
}
|
|
|
|
lfs3_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_file_open(&lfs3, &sim_files[j]->file, name,
|
|
LFS3_O_RDWR
|
|
| LFS3_O_CREAT
|
|
| ((FLUSH) ? LFS3_O_FLUSH : 0)) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (!exist || sticky) {
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE)
|
|
=> SIZE;
|
|
}
|
|
|
|
// open in our sim
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->sticky = sticky;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// insert into our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// write to the file
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0;
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// update in our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
// no longer sticky
|
|
sim_isstickys[k] = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
// new prng
|
|
sim_files[k]->prng = wprng;
|
|
// no longer sticky
|
|
sim_files[k]->sticky = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
lfs3_size_t sticky = sim_files[j]->sticky;
|
|
lfs3_size_t zombie = sim_files[j]->zombie;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// update our sim
|
|
if (sticky && !zombie) {
|
|
// orphaned?
|
|
bool orphan = true;
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
orphan = false;
|
|
}
|
|
}
|
|
|
|
// if we were never synced, delete from sim
|
|
if (orphan) {
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
memmove(&sim[k], &sim[k+1],
|
|
(sim_size-(k+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k], &sim_prngs[k+1],
|
|
(sim_size-(k+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[k], &sim_isdirs[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool sticky = sim_isstickys[j];
|
|
bool dir = sim_isdirs[j];
|
|
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// type mismatch?
|
|
if (sim_isdirs[k] != dir) {
|
|
goto nonsense;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
|
|
// update our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng/sticky/dir
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = dir;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-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;
|
|
'''
|
|
|