458fe16f38
Metastability is a rather nasty error condition where successive reads
to a memory location may return different values, either due to bus
issues or a failed prog. It's a tricky error condition to detect, and
one that ckreads was, in theory, supposed to help with.
To help test metastability (and other single-bit errors), emubd gained
several new features:
- LFS_EMUBD_BADBLOCK_PROGFLIP - Prog flips a bit
- LFS_EMUBD_BADBLOCK_READFLIP - Read flips a bit sometimes
- LFS_EMUBD_POWERLOSS_METASTABLE - Reads may flip a bit
These only affect a single bit in a given block, but by randomizing
which bit during every erase (and exhaustive bit testing in test_ck) we
should still see some fairly interesting bit-error patterns over time.
It's a bit difficult to test with more than a single bit error because
you can quickly find checksum/parity collisions when fuzz testing. But
there may be other interesting error patterns to look at in the future?
Also the erase_cycles implementation got a bit of a rework since it was
lopsided previously (progs/reads would always error before erases). And
since I was messing with emubd's internals I added lfs_emubd_markbad/
markgood and a few other convenience functions that seem useful:
- lfs_emubd_seed - Manually set the prng, needed in test_ck actually
- lfs_emubd_markbad - Mark block as bad, same as wear=-1
- lfs_emubd_markgood - Mark block as good, same as wear=0
- lfs_emubd_badbit - Get which big failed
- lfs_emubd_setbadbit - Set which bit will fail
- lfs_emubd_randomizebadbit - Randomize bad bit on erase
- lfs_emubd_markbadbit - Mark bit as bad, same as setbadbit+markbad
---
The intention of this new metastability emulation was to extend test_ck
to test ckreads/ckprogs. This went... interestingly.
The good news, the new emulation and tests worked quite well. They were
able to quite quickly show that ckreads is fundamentally not able to
detect all single-bit errors in our current design.
The problem boils down to the fact that the location of our parity bits
depends on the tag's leb128-encoded size. If a bit flip changes this
size field, we end up with a new parity bit, which 50/50 may or may not
detect the error.
For example, one bit flip:
40 0c 00 12 80 0d ff ff
'----.----' ^--------------------.
'- altble 0xc w0 -18 parity=1
40 0c 80 12 80 0d ff ff
'-------.-------' ^----------------------.
'- altble 0xc w2304 -1664 parity=1
This doesn't make ckreads _completely_ useless, just mostly useless. We
can still use it to check parity bits, but without a systematic proof.
But there's enough problems with ckreads: performance, RAM, code, etc,
that I think it may just be an interesting proof-of-concept and not
something users should actually use. Checking reads in the bd-layer
solves all of these problems...
---
At the very least ckprogs gets better testing, thanks to new tests in
test_ck and the addition of LFS_EMUBD_BADBLOCK_PROGFLIP in
test_badblocks.
The extra testing also found a ckprog/ckread hole in that we don't
ckprog/ckread during lfsr_format! I fixed this by making lfsr_format
always use ckprogs/ckreads if available, but maybe lfsr_format should
take its own set of flags?
Funnily enough this had no impact on code size since it probably just
changed the constant in a constant pool:
code stack
before: 37872 3048
after: 37872 (+0.0%) 3048 (+0.0%)
5574 lines
185 KiB
TOML
5574 lines
185 KiB
TOML
# Bad-block related tests
|
|
after = [
|
|
'test_dirs',
|
|
'test_files',
|
|
'test_fwrite',
|
|
'test_forphans',
|
|
'test_traversal',
|
|
'test_gc',
|
|
'test_mount',
|
|
'test_ck',
|
|
'test_compat',
|
|
]
|
|
|
|
|
|
## Single badblock tests
|
|
#
|
|
# first test with every possible single badblock
|
|
|
|
# B-tree's ridiculous branching factor is great for performance, but it makes
|
|
# them a bit of a pain to test, here we test them explicitly
|
|
[cases.test_badblocks_single_btree_many]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
# maximize lookahead buffer to avoid alloc scans
|
|
defines.LOOKAHEAD_SIZE = 'lfs_alignup(BLOCK_COUNT / 8, 8)'
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 0;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test creating a btree
|
|
lfs_t lfs;
|
|
lfs_init(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.window = 2;
|
|
lfs.lookahead.off = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count-2);
|
|
lfs_alloc_ckpoint(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL();
|
|
|
|
// set up a simulation to compare against
|
|
char *sim = malloc(N);
|
|
lfs_size_t sim_size = 0;
|
|
memset(sim, 0, N);
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
|
|
// add to btree
|
|
lfsr_btree_commit(&lfs, &btree, bid, LFSR_ATTRS(
|
|
LFSR_ATTR(
|
|
LFSR_TAG_DATA, +1,
|
|
LFSR_DATA_BUF(&(uint8_t){'a'+(i % 26)}, 1)))) => 0;
|
|
|
|
// add to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
sim[bid] = 'a'+(i % 26);
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.weight,
|
|
btree.blocks[0],
|
|
btree.trunk);
|
|
assert(btree.weight == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_lookup(&lfs, &btree, i,
|
|
&tag_, &weight_, &data_) => 0;
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_lookup(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
lfs_deinit(&lfs) => 0;
|
|
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
# with dirs
|
|
[cases.test_badblocks_single_dir_many]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 2;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test creating directories
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// make this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || (TEST_PLS && err == LFS_ERR_EXIST));
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// check that our mkdir worked
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_open(&lfs, &dir, name) => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
# fuzz dirs
|
|
[cases.test_badblocks_single_dir_fuzz]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
defines.OPS = '2*N'
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 2;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test fuzz with dirs
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose a pseudo-random op, either mkdir, remove, or rename
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number, truncate to 3 hexadecimals
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// do nothing
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a directory here
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || err == LFS_ERR_EXIST);
|
|
|
|
} else if (op == 1) {
|
|
// choose a pseudo-random entry to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
|
|
// remove this directory
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
} else {
|
|
// choose a pseudo-random entry to rename, and a pseudo-random
|
|
// number to rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// already seen and not a noop?
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// just delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
sim[k] = y;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this directory
|
|
char old_name[256];
|
|
sprintf(old_name, "dir%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "dir%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// test that our directories match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
char name2[256];
|
|
sprintf(name2, "dir%03x", sim[j]);
|
|
assert(strcmp(info.name, name2) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
# with files
|
|
[cases.test_badblocks_single_file_many]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 2;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test creating files
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check that our writes worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
# fuzz files
|
|
[cases.test_badblocks_single_file_fuzz]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 2;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test fuzz with files
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
# with more complex file writes
|
|
[cases.test_badblocks_single_fwrite_fuzz]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.OPS = 20
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# chunk is more an upper limit here
|
|
defines.CHUNK = 64
|
|
# INIT=0 => no init
|
|
# INIT=1 => fill with data
|
|
# INIT=2 => truncate to size
|
|
defines.INIT = [0, 1, 2]
|
|
defines.SYNC = [false, true]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'CHUNK <= SIZE',
|
|
# this just saves testing time
|
|
'SIZE <= 4*1024*FRAGMENT_SIZE',
|
|
]
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 2;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test with complex file writes
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "hello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
// simulate our file in ram
|
|
uint8_t sim[SIZE];
|
|
lfs_off_t size;
|
|
uint32_t prng = SEED;
|
|
if (INIT == 0) {
|
|
memset(sim, 0, SIZE);
|
|
size = 0;
|
|
} else if (INIT == 1) {
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
|
size = SIZE;
|
|
} else {
|
|
memset(sim, 0, SIZE);
|
|
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
|
size = SIZE;
|
|
}
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose a random location
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
// and a random size, up to the chunk size
|
|
lfs_size_t chunk = lfs_min(
|
|
TEST_PRNG(&prng) % CHUNK,
|
|
SIZE - off);
|
|
|
|
// update sim
|
|
for (lfs_size_t j = 0; j < chunk; j++) {
|
|
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
if (chunk != 0) {
|
|
size = lfs_max(size, off+chunk);
|
|
}
|
|
|
|
// update file
|
|
lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check our file with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
|
|
// and with dir read
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// try reading our file
|
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => size;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size;
|
|
// does our file match our simulation?
|
|
assert(memcmp(rbuf, sim, size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
# with orphans, zombies, etc
|
|
[cases.test_badblocks_single_orphanzombie_fuzz]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 2;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test with orphans, zombies, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 5;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE)
|
|
=> SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
# with orphans, zombies, dirs, etc
|
|
[cases.test_badblocks_single_orphanzombiedir_fuzz]
|
|
defines.BADBLOCK = -1
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test all possible bad blocks
|
|
for (lfs_size_t i = 2;
|
|
i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1);
|
|
i++) {
|
|
lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK;
|
|
// mark our badblock as bad
|
|
lfs_emubd_markbad(CFG, badblock) => 0;
|
|
printf("--- badblock: 0x%x ---\n", badblock);
|
|
|
|
// test with orphans, zombies, dirs, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isdirs = malloc(N*sizeof(bool));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 8;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE)
|
|
=> SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool isdir = sim_isdirs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// type mismatch?
|
|
if (sim_isdirs[k] != isdir) {
|
|
goto nonsense;
|
|
}
|
|
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = isdir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
|
|
// toss a directory into the mix
|
|
} else if (op == 5) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// insert into our sim, use negative numbers for dirs
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
goto nonsense;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = 0;
|
|
sim_isdirs[k] = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// make the directory
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim_isdirs[j]) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY)
|
|
=> LFS_ERR_ISDIR;
|
|
|
|
} else {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// reset badblock
|
|
lfs_emubd_markgood(CFG, badblock) => 0;
|
|
}
|
|
'''
|
|
|
|
|
|
|
|
## Badblock regions
|
|
#
|
|
# Test with a region of badblocks, this chould cause cascading failures,
|
|
# which can be tricky
|
|
|
|
# B-tree's ridiculous branching factor is great for performance, but it makes
|
|
# them a bit of a pain to test, here we test them explicitly
|
|
[cases.test_badblocks_region_btree_many]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
# maximize lookahead buffer to avoid alloc scans
|
|
defines.LOOKAHEAD_SIZE = 'lfs_alignup(BLOCK_COUNT / 8, 8)'
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
} else {
|
|
lfs_emubd_markbad(CFG, i + BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
|
|
// test creating a btree
|
|
lfs_t lfs;
|
|
lfs_init(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.window = 2;
|
|
lfs.lookahead.off = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count-2);
|
|
lfs_alloc_ckpoint(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL();
|
|
|
|
// set up a simulation to compare against
|
|
char *sim = malloc(N);
|
|
lfs_size_t sim_size = 0;
|
|
memset(sim, 0, N);
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
|
|
// add to btree
|
|
lfsr_btree_commit(&lfs, &btree, bid, LFSR_ATTRS(
|
|
LFSR_ATTR(
|
|
LFSR_TAG_DATA, +1,
|
|
LFSR_DATA_BUF(&(uint8_t){'a'+(i % 26)}, 1)))) => 0;
|
|
|
|
// add to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
sim[bid] = 'a'+(i % 26);
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.weight,
|
|
btree.blocks[0],
|
|
btree.trunk);
|
|
assert(btree.weight == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_lookup(&lfs, &btree, i,
|
|
&tag_, &weight_, &data_) => 0;
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_lookup(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
lfs_deinit(&lfs) => 0;
|
|
'''
|
|
|
|
# with dirs
|
|
[cases.test_badblocks_region_dir_many]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (i >= 2) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
}
|
|
} else {
|
|
if (i+BLOCK_COUNT/2 >= 2) {
|
|
lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test creating directories
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// make this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || (TEST_PLS && err == LFS_ERR_EXIST));
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// check that our mkdir worked
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_open(&lfs, &dir, name) => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz dirs
|
|
[cases.test_badblocks_region_dir_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
defines.OPS = '2*N'
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (i >= 2) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
}
|
|
} else {
|
|
if (i+BLOCK_COUNT/2 >= 2) {
|
|
lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test fuzz with dirs
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose a pseudo-random op, either mkdir, remove, or rename
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number, truncate to 3 hexadecimals
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// do nothing
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a directory here
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || err == LFS_ERR_EXIST);
|
|
|
|
} else if (op == 1) {
|
|
// choose a pseudo-random entry to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
|
|
// remove this directory
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
} else {
|
|
// choose a pseudo-random entry to rename, and a pseudo-random
|
|
// number to rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// already seen and not a noop?
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// just delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
sim[k] = y;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this directory
|
|
char old_name[256];
|
|
sprintf(old_name, "dir%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "dir%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// test that our directories match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
char name2[256];
|
|
sprintf(name2, "dir%03x", sim[j]);
|
|
assert(strcmp(info.name, name2) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with files
|
|
[cases.test_badblocks_region_file_many]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (i >= 2) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
}
|
|
} else {
|
|
if (i+BLOCK_COUNT/2 >= 2) {
|
|
lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test creating files
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check that our writes worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz files
|
|
[cases.test_badblocks_region_file_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (i >= 2) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
}
|
|
} else {
|
|
if (i+BLOCK_COUNT/2 >= 2) {
|
|
lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test fuzz with files
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with more complex file writes
|
|
[cases.test_badblocks_region_fwrite_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.OPS = 20
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# chunk is more an upper limit here
|
|
defines.CHUNK = [32, 8, 1]
|
|
# INIT=0 => no init
|
|
# INIT=1 => fill with data
|
|
# INIT=2 => truncate to size
|
|
defines.INIT = [0, 1, 2]
|
|
defines.SYNC = [false, true]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'CHUNK <= SIZE',
|
|
# this just saves testing time
|
|
'SIZE <= 4*1024*FRAGMENT_SIZE',
|
|
]
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (i >= 2) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
}
|
|
} else {
|
|
if (i+BLOCK_COUNT/2 >= 2) {
|
|
lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test with complex file writes
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "hello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
// simulate our file in ram
|
|
uint8_t sim[SIZE];
|
|
lfs_off_t size;
|
|
uint32_t prng = SEED;
|
|
if (INIT == 0) {
|
|
memset(sim, 0, SIZE);
|
|
size = 0;
|
|
} else if (INIT == 1) {
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
|
size = SIZE;
|
|
} else {
|
|
memset(sim, 0, SIZE);
|
|
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
|
size = SIZE;
|
|
}
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose a random location
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
// and a random size, up to the chunk size
|
|
lfs_size_t chunk = lfs_min(
|
|
TEST_PRNG(&prng) % CHUNK,
|
|
SIZE - off);
|
|
|
|
// update sim
|
|
for (lfs_size_t j = 0; j < chunk; j++) {
|
|
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
if (chunk != 0) {
|
|
size = lfs_max(size, off+chunk);
|
|
}
|
|
|
|
// update file
|
|
lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check our file with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
|
|
// and with dir read
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// try reading our file
|
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => size;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size;
|
|
// does our file match our simulation?
|
|
assert(memcmp(rbuf, sim, size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with orphans, zombies, etc
|
|
[cases.test_badblocks_region_orphanzombie_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (i >= 2) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
}
|
|
} else {
|
|
if (i+BLOCK_COUNT/2 >= 2) {
|
|
lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test with orphans, zombies, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 5;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with orphans, zombies, dirs, etc
|
|
[cases.test_badblocks_region_orphanzombiedir_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (i >= 2) {
|
|
lfs_emubd_markbad(CFG, i) => 0;
|
|
}
|
|
} else {
|
|
if (i+BLOCK_COUNT/2 >= 2) {
|
|
lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test with orphans, zombies, dirs, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isdirs = malloc(N*sizeof(bool));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 8;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool isdir = sim_isdirs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// type mismatch?
|
|
if (sim_isdirs[k] != isdir) {
|
|
goto nonsense;
|
|
}
|
|
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = isdir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
|
|
// toss a directory into the mix
|
|
} else if (op == 5) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// insert into our sim, use negative numbers for dirs
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
goto nonsense;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = 0;
|
|
sim_isdirs[k] = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// make the directory
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim_isdirs[j]) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
|
|
} else {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
## Alternating badblocks
|
|
#
|
|
# Test alternating badblocks, this can be difficult for pair allocations
|
|
|
|
# B-tree's ridiculous branching factor is great for performance, but it makes
|
|
# them a bit of a pain to test, here we test them explicitly
|
|
[cases.test_badblocks_alternating_btree_many]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
# maximize lookahead buffer to avoid alloc scans
|
|
defines.LOOKAHEAD_SIZE = 'lfs_alignup(BLOCK_COUNT / 8, 8)'
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
} else {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
|
|
// test creating a btree
|
|
lfs_t lfs;
|
|
lfs_init(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.window = 2;
|
|
lfs.lookahead.off = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count-2);
|
|
lfs_alloc_ckpoint(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL();
|
|
|
|
// set up a simulation to compare against
|
|
char *sim = malloc(N);
|
|
lfs_size_t sim_size = 0;
|
|
memset(sim, 0, N);
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
|
|
// add to btree
|
|
lfsr_btree_commit(&lfs, &btree, bid, LFSR_ATTRS(
|
|
LFSR_ATTR(
|
|
LFSR_TAG_DATA, +1,
|
|
LFSR_DATA_BUF(&(uint8_t){'a'+(i % 26)}, 1)))) => 0;
|
|
|
|
// add to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
sim[bid] = 'a'+(i % 26);
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.weight,
|
|
btree.blocks[0],
|
|
btree.trunk);
|
|
assert(btree.weight == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_lookup(&lfs, &btree, i,
|
|
&tag_, &weight_, &data_) => 0;
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_lookup(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
lfs_deinit(&lfs) => 0;
|
|
'''
|
|
|
|
# with dirs
|
|
[cases.test_badblocks_alternating_dir_many]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (2*i+0 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
}
|
|
} else {
|
|
if (2*i+1 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test creating directories
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// make this many directories
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || (TEST_PLS && err == LFS_ERR_EXIST));
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// check that our mkdir worked
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfsr_dir_open(&lfs, &dir, name) => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz dirs
|
|
[cases.test_badblocks_alternating_dir_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
defines.OPS = '2*N'
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (2*i+0 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
}
|
|
} else {
|
|
if (2*i+1 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test fuzz with dirs
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose a pseudo-random op, either mkdir, remove, or rename
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number, truncate to 3 hexadecimals
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// do nothing
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a directory here
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
int err = lfsr_mkdir(&lfs, name);
|
|
assert(!err || err == LFS_ERR_EXIST);
|
|
|
|
} else if (op == 1) {
|
|
// choose a pseudo-random entry to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
|
|
// remove this directory
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
} else {
|
|
// choose a pseudo-random entry to rename, and a pseudo-random
|
|
// number to rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// already seen and not a noop?
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// just delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
sim[k] = y;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this directory
|
|
char old_name[256];
|
|
sprintf(old_name, "dir%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "dir%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs.grm_p[0] == 0);
|
|
|
|
// test that our directories match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
char name2[256];
|
|
sprintf(name2, "dir%03x", sim[j]);
|
|
assert(strcmp(info.name, name2) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with files
|
|
[cases.test_badblocks_alternating_file_many]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (2*i+0 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
}
|
|
} else {
|
|
if (2*i+1 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test creating files
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check that our writes worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz files
|
|
[cases.test_badblocks_alternating_file_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (2*i+0 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
}
|
|
} else {
|
|
if (2*i+1 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test fuzz with files
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with more complex file writes
|
|
[cases.test_badblocks_alternating_fwrite_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.OPS = 20
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# chunk is more an upper limit here
|
|
defines.CHUNK = [32, 8, 1]
|
|
# INIT=0 => no init
|
|
# INIT=1 => fill with data
|
|
# INIT=2 => truncate to size
|
|
defines.INIT = [0, 1, 2]
|
|
defines.SYNC = [false, true]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = [
|
|
'CHUNK <= SIZE',
|
|
# this just saves testing time
|
|
'SIZE <= 4*1024*FRAGMENT_SIZE',
|
|
]
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (2*i+0 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
}
|
|
} else {
|
|
if (2*i+1 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test with complex file writes
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "hello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
// simulate our file in ram
|
|
uint8_t sim[SIZE];
|
|
lfs_off_t size;
|
|
uint32_t prng = SEED;
|
|
if (INIT == 0) {
|
|
memset(sim, 0, SIZE);
|
|
size = 0;
|
|
} else if (INIT == 1) {
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
sim[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE;
|
|
size = SIZE;
|
|
} else {
|
|
memset(sim, 0, SIZE);
|
|
lfsr_file_truncate(&lfs, &file, SIZE) => 0;
|
|
size = SIZE;
|
|
}
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
// choose a random location
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
// and a random size, up to the chunk size
|
|
lfs_size_t chunk = lfs_min(
|
|
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
|
|
SIZE - off);
|
|
|
|
// update sim
|
|
for (lfs_size_t j = 0; j < chunk; j++) {
|
|
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
size = lfs_max(size, off+chunk);
|
|
|
|
// update file
|
|
lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
|
|
|
|
// sync?
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
}
|
|
|
|
// check our file with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
|
|
// and with dir read
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == size);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// try reading our file
|
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => size;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size;
|
|
// does our file match our simulation?
|
|
assert(memcmp(rbuf, sim, size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with orphans, zombies, etc
|
|
[cases.test_badblocks_alternating_orphanzombie_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (2*i+0 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
}
|
|
} else {
|
|
if (2*i+1 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test with orphans, zombies, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 5;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# with orphans, zombies, dirs, etc
|
|
[cases.test_badblocks_alternating_orphanzombiedir_fuzz]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
'LFS_EMUBD_BADBLOCK_PROGFLIP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
defines.MIRROR = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 42
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
// test a large region of bad blocks
|
|
for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) {
|
|
// mark our badblock as bad
|
|
if (!MIRROR) {
|
|
if (2*i+0 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+0) => 0;
|
|
}
|
|
} else {
|
|
if (2*i+1 >= 2) {
|
|
lfs_emubd_markbad(CFG, 2*i+1) => 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// test with orphans, zombies, dirs, etc
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isdirs = malloc(N*sizeof(bool));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 8;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool isdir = sim_isdirs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// type mismatch?
|
|
if (sim_isdirs[k] != isdir) {
|
|
goto nonsense;
|
|
}
|
|
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = isdir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
|
|
// toss a directory into the mix
|
|
} else if (op == 5) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// insert into our sim, use negative numbers for dirs
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
goto nonsense;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = 0;
|
|
sim_isdirs[k] = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// make the directory
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim_isdirs[j]) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
|
|
} else {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
## other corner cases
|
|
|
|
# test formatting with 0 or 1 bad, this should just error
|
|
[cases.test_badblocks_mrootanchor_format]
|
|
defines.BADBLOCKS = [0x1, 0x2, 0x3]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
]
|
|
code = '''
|
|
if (BADBLOCKS & 0x1) {
|
|
lfs_emubd_markbad(CFG, 0) => 0;
|
|
}
|
|
if (BADBLOCKS & 0x2) {
|
|
lfs_emubd_markbad(CFG, 1) => 0;
|
|
}
|
|
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => LFS_ERR_CORRUPT;
|
|
'''
|
|
|
|
# test blocks 0 or 1 going bad, this should just error
|
|
[cases.test_badblocks_mrootanchor_wear]
|
|
defines.BADBLOCKS = [0x1, 0x2]
|
|
defines.BADBLOCK_BEHAVIOR = [
|
|
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
'LFS_EMUBD_BADBLOCK_READERROR',
|
|
'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
]
|
|
# we need prog checking to detect read errors
|
|
defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
|
|
if (BADBLOCKS & 0x1) {
|
|
lfs_emubd_markbad(CFG, 0) => 0;
|
|
}
|
|
if (BADBLOCKS & 0x2) {
|
|
lfs_emubd_markbad(CFG, 1) => 0;
|
|
}
|
|
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((CKPROGS) ? LFS_M_CKPROGS : 0),
|
|
CFG) => 0;
|
|
|
|
for (lfs_size_t i = 0;; i++) {
|
|
// this should eventually fail
|
|
assert(i <= BLOCK_COUNT);
|
|
|
|
int err = lfsr_mkdir(&lfs, "hi");
|
|
assert(!err || err == LFS_ERR_NOSPC);
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
|
|
err = lfsr_remove(&lfs, "hi");
|
|
assert(!err || err == LFS_ERR_NOSPC);
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## bad blocks with block cycles should be tested in test_relocations
|
|
#if = '(int32_t)BLOCK_CYCLES == -1'
|
|
#
|
|
#[cases.test_badblocks_single]
|
|
#defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
|
#defines.ERASE_CYCLES = 0xffffffff
|
|
#defines.ERASE_VALUE = [0x00, 0xff, -1]
|
|
#defines.BADBLOCK_BEHAVIOR = [
|
|
# 'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_READERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
#]
|
|
#defines.NAMEMULT = 64
|
|
#defines.FILEMULT = 1
|
|
#code = '''
|
|
# for (lfs_block_t badblock = 2; badblock < BLOCK_COUNT; badblock++) {
|
|
# lfs_emubd_setwear(cfg, badblock-1, 0) => 0;
|
|
# lfs_emubd_setwear(cfg, badblock, 0xffffffff) => 0;
|
|
#
|
|
# lfs_t lfs;
|
|
# lfs_format(&lfs, cfg) => 0;
|
|
#
|
|
# lfs_mount(&lfs, cfg) => 0;
|
|
# for (int i = 1; i < 10; i++) {
|
|
# uint8_t buffer[1024];
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j] = '0'+i;
|
|
# }
|
|
# buffer[NAMEMULT] = '\0';
|
|
# lfs_mkdir(&lfs, (char*)buffer) => 0;
|
|
#
|
|
# buffer[NAMEMULT] = '/';
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j+NAMEMULT+1] = '0'+i;
|
|
# }
|
|
# buffer[2*NAMEMULT+1] = '\0';
|
|
# lfs_file_t file;
|
|
# lfs_file_open(&lfs, &file, (char*)buffer,
|
|
# LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
#
|
|
# lfs_size_t size = NAMEMULT;
|
|
# for (int j = 0; j < i*FILEMULT; j++) {
|
|
# lfs_file_write(&lfs, &file, buffer, size) => size;
|
|
# }
|
|
#
|
|
# lfs_file_close(&lfs, &file) => 0;
|
|
# }
|
|
# lfs_unmount(&lfs) => 0;
|
|
#
|
|
# lfs_mount(&lfs, cfg) => 0;
|
|
# for (int i = 1; i < 10; i++) {
|
|
# uint8_t buffer[1024];
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j] = '0'+i;
|
|
# }
|
|
# buffer[NAMEMULT] = '\0';
|
|
# struct lfs_info info;
|
|
# lfs_stat(&lfs, (char*)buffer, &info) => 0;
|
|
# info.type => LFS_TYPE_DIR;
|
|
#
|
|
# buffer[NAMEMULT] = '/';
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j+NAMEMULT+1] = '0'+i;
|
|
# }
|
|
# buffer[2*NAMEMULT+1] = '\0';
|
|
# lfs_file_t file;
|
|
# lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
|
|
#
|
|
# int size = NAMEMULT;
|
|
# for (int j = 0; j < i*FILEMULT; j++) {
|
|
# uint8_t rbuffer[1024];
|
|
# lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
|
# memcmp(buffer, rbuffer, size) => 0;
|
|
# }
|
|
#
|
|
# lfs_file_close(&lfs, &file) => 0;
|
|
# }
|
|
# lfs_unmount(&lfs) => 0;
|
|
# }
|
|
#'''
|
|
#
|
|
#[cases.test_badblocks_region_corruption] # (causes cascading failures)
|
|
#defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
|
#defines.ERASE_CYCLES = 0xffffffff
|
|
#defines.ERASE_VALUE = [0x00, 0xff, -1]
|
|
#defines.BADBLOCK_BEHAVIOR = [
|
|
# 'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_READERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
#]
|
|
#defines.NAMEMULT = 64
|
|
#defines.FILEMULT = 1
|
|
#code = '''
|
|
# for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
|
|
# lfs_emubd_setwear(cfg, i+2, 0xffffffff) => 0;
|
|
# }
|
|
#
|
|
# lfs_t lfs;
|
|
# lfs_format(&lfs, cfg) => 0;
|
|
#
|
|
# lfs_mount(&lfs, cfg) => 0;
|
|
# for (int i = 1; i < 10; i++) {
|
|
# uint8_t buffer[1024];
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j] = '0'+i;
|
|
# }
|
|
# buffer[NAMEMULT] = '\0';
|
|
# lfs_mkdir(&lfs, (char*)buffer) => 0;
|
|
#
|
|
# buffer[NAMEMULT] = '/';
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j+NAMEMULT+1] = '0'+i;
|
|
# }
|
|
# buffer[2*NAMEMULT+1] = '\0';
|
|
# lfs_file_t file;
|
|
# lfs_file_open(&lfs, &file, (char*)buffer,
|
|
# LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
#
|
|
# lfs_size_t size = NAMEMULT;
|
|
# for (int j = 0; j < i*FILEMULT; j++) {
|
|
# lfs_file_write(&lfs, &file, buffer, size) => size;
|
|
# }
|
|
#
|
|
# lfs_file_close(&lfs, &file) => 0;
|
|
# }
|
|
# lfs_unmount(&lfs) => 0;
|
|
#
|
|
# lfs_mount(&lfs, cfg) => 0;
|
|
# for (int i = 1; i < 10; i++) {
|
|
# uint8_t buffer[1024];
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j] = '0'+i;
|
|
# }
|
|
# buffer[NAMEMULT] = '\0';
|
|
# struct lfs_info info;
|
|
# lfs_stat(&lfs, (char*)buffer, &info) => 0;
|
|
# info.type => LFS_TYPE_DIR;
|
|
#
|
|
# buffer[NAMEMULT] = '/';
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j+NAMEMULT+1] = '0'+i;
|
|
# }
|
|
# buffer[2*NAMEMULT+1] = '\0';
|
|
# lfs_file_t file;
|
|
# lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
|
|
#
|
|
# lfs_size_t size = NAMEMULT;
|
|
# for (int j = 0; j < i*FILEMULT; j++) {
|
|
# uint8_t rbuffer[1024];
|
|
# lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
|
# memcmp(buffer, rbuffer, size) => 0;
|
|
# }
|
|
#
|
|
# lfs_file_close(&lfs, &file) => 0;
|
|
# }
|
|
# lfs_unmount(&lfs) => 0;
|
|
#'''
|
|
#
|
|
#[cases.test_badblocks_alternating_corruption] # (causes cascading failures)
|
|
#defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
|
#defines.ERASE_CYCLES = 0xffffffff
|
|
#defines.ERASE_VALUE = [0x00, 0xff, -1]
|
|
#defines.BADBLOCK_BEHAVIOR = [
|
|
# 'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_READERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
#]
|
|
#defines.NAMEMULT = 64
|
|
#defines.FILEMULT = 1
|
|
#code = '''
|
|
# for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
|
|
# lfs_emubd_setwear(cfg, (2*i) + 2, 0xffffffff) => 0;
|
|
# }
|
|
#
|
|
# lfs_t lfs;
|
|
# lfs_format(&lfs, cfg) => 0;
|
|
#
|
|
# lfs_mount(&lfs, cfg) => 0;
|
|
# for (int i = 1; i < 10; i++) {
|
|
# uint8_t buffer[1024];
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j] = '0'+i;
|
|
# }
|
|
# buffer[NAMEMULT] = '\0';
|
|
# lfs_mkdir(&lfs, (char*)buffer) => 0;
|
|
#
|
|
# buffer[NAMEMULT] = '/';
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j+NAMEMULT+1] = '0'+i;
|
|
# }
|
|
# buffer[2*NAMEMULT+1] = '\0';
|
|
# lfs_file_t file;
|
|
# lfs_file_open(&lfs, &file, (char*)buffer,
|
|
# LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
#
|
|
# lfs_size_t size = NAMEMULT;
|
|
# for (int j = 0; j < i*FILEMULT; j++) {
|
|
# lfs_file_write(&lfs, &file, buffer, size) => size;
|
|
# }
|
|
#
|
|
# lfs_file_close(&lfs, &file) => 0;
|
|
# }
|
|
# lfs_unmount(&lfs) => 0;
|
|
#
|
|
# lfs_mount(&lfs, cfg) => 0;
|
|
# for (int i = 1; i < 10; i++) {
|
|
# uint8_t buffer[1024];
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j] = '0'+i;
|
|
# }
|
|
# buffer[NAMEMULT] = '\0';
|
|
# struct lfs_info info;
|
|
# lfs_stat(&lfs, (char*)buffer, &info) => 0;
|
|
# info.type => LFS_TYPE_DIR;
|
|
#
|
|
# buffer[NAMEMULT] = '/';
|
|
# for (int j = 0; j < NAMEMULT; j++) {
|
|
# buffer[j+NAMEMULT+1] = '0'+i;
|
|
# }
|
|
# buffer[2*NAMEMULT+1] = '\0';
|
|
# lfs_file_t file;
|
|
# lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
|
|
#
|
|
# lfs_size_t size = NAMEMULT;
|
|
# for (int j = 0; j < i*FILEMULT; j++) {
|
|
# uint8_t rbuffer[1024];
|
|
# lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
|
# memcmp(buffer, rbuffer, size) => 0;
|
|
# }
|
|
#
|
|
# lfs_file_close(&lfs, &file) => 0;
|
|
# }
|
|
# lfs_unmount(&lfs) => 0;
|
|
#'''
|
|
#
|
|
## other corner cases
|
|
#[cases.test_badblocks_superblocks] # (corrupt 1 or 0)
|
|
#defines.ERASE_CYCLES = 0xffffffff
|
|
#defines.ERASE_VALUE = [0x00, 0xff, -1]
|
|
#defines.BADBLOCK_BEHAVIOR = [
|
|
# 'LFS_EMUBD_BADBLOCK_PROGERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASEERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_READERROR',
|
|
# 'LFS_EMUBD_BADBLOCK_PROGNOOP',
|
|
# 'LFS_EMUBD_BADBLOCK_ERASENOOP',
|
|
#]
|
|
#code = '''
|
|
# lfs_emubd_setwear(cfg, 0, 0xffffffff) => 0;
|
|
# lfs_emubd_setwear(cfg, 1, 0xffffffff) => 0;
|
|
#
|
|
# lfs_t lfs;
|
|
# lfs_format(&lfs, cfg) => LFS_ERR_NOSPC;
|
|
# lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
|
|
#'''
|