0f4ad6d842
So now the following creates a reg file (instead of just a stickynote):
lfsr_file_open(&lfs, &file, "test.txt",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_SYNC) => 0;
// powerloss!!
struct lfsr_info info;
lfsr_stat(&lfs, "test.txt", &info) => 0; // LFS_ERR_NOENT before
assert(info.type == LFS_TYPE_REG);
This hopefully results in more intuitive behavior around lfsr_file_open
with LFS_O_SYNC. Which is important as LFS_O_SYNC is often used as an
escape hatch to avoid needing to reason about syncing things when
performance is not a big concern.
Unfortunately this does come with a surprisingly big code/stack cost,
but I'm thinking of putting these flags (LFS_O_FLUSH/LFS_O_SYNC) behind
ifdefs anyways (LFS_MAYBE_SYNC?):
code stack ctx
before: 35780 2440 640
after: 35836 (+0.2%) 2488 (+2.0%) 640 (+0.0%)
Also added some more tests to make sure these open+LFS_O_SYNC cases are
explicitly covered:
- test_fsync_sync_o_wrr
- test_fsync_sync_o_wwrr
- test_fsync_desync_o_wdwrr
- test_fsync_resync_o_wdwyrr
This does make a bit of a mess when you combined LFS_O_SYNC +
LFS_O_DESYNC. What exactly should a SYNC + DESYNC file look like?
For now I've just made LFS_O_SYNC + LFS_O_DESYNC behave as if you opened
a file with LFS_O_SYNC and then immediately called lfsr_file_desync on
it. So it doesn't receive broadcasts, but _does_ create the reg file,
and _does_ sync on first write, clearing the desync flag.
But this may be worth revisiting. Maybe LFS_O_DESYNC files shouldn't
have their desync flags cleared unless lfsr_file_sync is explicitly
called? Or maybe LFS_O_SYNC + LFS_O_DESYNC should just be an error?
Unsure...
5488 lines
183 KiB
TOML
5488 lines
183 KiB
TOML
# Test multiple open file handles in different r/w configurations
|
|
after = 'test_fwrite'
|
|
|
|
|
|
# Some specific tests
|
|
[cases.test_fsync_sync_wrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
|
|
// should not show up in b yet
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that LFS_O_SYNC/LFS_O_FLUSH flags work as expected
|
|
[cases.test_fsync_sync_o_wrr]
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 2, 3]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
// LFS_O_SYNC/LFS_M_SYNC should immediately create the file
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_CREAT
|
|
| LFS_O_EXCL
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "jello", &info) => 0;
|
|
assert(strcmp(info.name, "jello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
// create the other files
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
|
|
// should immediately show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should immediately show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// should immediately show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that LFS_O_SYNC/LFS_O_FLUSH flags work as expected
|
|
[cases.test_fsync_sync_o_wwrr]
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 2, 3]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
// LFS_O_SYNC/LFS_M_SYNC should immediately create the file
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_CREAT
|
|
| LFS_O_EXCL
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "jello", &info) => 0;
|
|
assert(strcmp(info.name, "jello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
// create the other files
|
|
lfsr_file_open(&lfs, &b, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// write to b
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a's contents were clobbered, so we should see b
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// should still have a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr_zero]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// truncate b, this gets tricky as we no longer have a struct
|
|
lfsr_file_truncate(&lfs, &b, 0) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our truncate should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// truncate a one last time
|
|
lfsr_file_truncate(&lfs, &a, 0) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// now our truncate should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a, this may be tricky since we haven't touched b
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b, this may be tricky since we haven't touched b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b one last time, this time with sync
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr_append]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test multiple readers, this shouldn't really have any issues
|
|
[cases.test_fsync_rrrr]
|
|
defines.R = 4
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// read R handles in parallel
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rrrr_fuzz]
|
|
defines.R = 4
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = SEED;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// read R handles in parallel
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test one writer, multiple readers
|
|
[cases.test_fsync_wrrr]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wrrr_fuzz]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple writers
|
|
[cases.test_fsync_wwww]
|
|
defines.W = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W handles in parallel
|
|
lfsr_file_t writers[W];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wwww_fuzz]
|
|
defines.W = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W files in parallel
|
|
lfsr_file_t writers[W];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writers[w], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, size) => size;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple writers and multiple readers
|
|
[cases.test_fsync_wwrr]
|
|
defines.W = 4
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W handles, R handles in parallel
|
|
lfsr_file_t writers[W];
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wwrr_fuzz]
|
|
defines.W = 4
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W files in parallel
|
|
lfsr_file_t writers[W];
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writers[w], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, size) => size;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rd/wrers
|
|
[cases.test_fsync_rwrw]
|
|
defines.RW = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK;
|
|
memcpy(&between[rw][i], wbuf, CHUNK);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rwrw_fuzz]
|
|
defines.RW = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rw files without fixed size
|
|
[cases.test_fsync_rwrw_sparse_fuzz]
|
|
defines.RW = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(100)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (SYNC) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rw files while also truncating/fruncating
|
|
[cases.test_fsync_rwtfrwtf_sparse_fuzz]
|
|
defines.RW = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 1, 2, 3]
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(100)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 4;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
|
|
// truncating?
|
|
} else if (op == 2) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// truncate
|
|
lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0;
|
|
if (FLUSH == 1) { // (flush does nothing)
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
size - between_size[rw]);
|
|
}
|
|
between_size[rw] = size;
|
|
|
|
// fruncating?
|
|
} else if (op == 3) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// fruncate
|
|
lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0;
|
|
if (FLUSH == 1) { // (flush does nothing)
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memmove(&between[rw][size - between_size[rw]],
|
|
between[rw],
|
|
between_size[rw]);
|
|
memset(between[rw],
|
|
0,
|
|
size - between_size[rw]);
|
|
} else {
|
|
memmove(between[rw],
|
|
&between[rw][between_size[rw] - size],
|
|
size);
|
|
}
|
|
between_size[rw] = size;
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (SYNC) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Desynced files make things interesting
|
|
|
|
# Some specific tests
|
|
[cases.test_fsync_desync_wdrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - desynced writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// now mark as desync, rewrite, and close
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// reopen a, mark as desync, rewrite a, and sync
|
|
lfsr_file_open(&lfs, &a, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// this sync should clear the desync
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// rewrite a, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wrrd]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - desynced reader
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// but not in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
|
|
// rewrite and sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
|
|
// rewrite and close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wwdrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, rewrite, and close
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen b, mark as desync, rewrite
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a, close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!"))
|
|
=> strlen("czesc!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
|
|
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
|
|
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that LFS_O_SYNC/LFS_O_FLUSH flags work as expected
|
|
[cases.test_fsync_desync_o_wdwrr]
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 2, 3]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// a - desynced writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
// LFS_O_SYNC/LFS_M_SYNC should immediately create the file
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_CREAT
|
|
| LFS_O_EXCL
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)
|
|
| LFS_O_DESYNC) => 0;
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "jello", &info) => 0;
|
|
assert(strcmp(info.name, "jello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
// create the other files
|
|
lfsr_file_open(&lfs, &b, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to b
|
|
lfsr_file_write(&lfs, &b, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should revert to a's contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// should still have a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should still have b, this may be surprising, but the implicit
|
|
// sync when we rewrote a clears the desync flag
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// desync a
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
// rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should revert to a's contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// should still have a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wwdrr_zero]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, truncate, and close, this gets tricky as we no
|
|
// longer have a struct
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_truncate(&lfs, &b, 0) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen b, mark as desync, truncate
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_truncate(&lfs, &b, 0) => 0;
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// truncate a, close
|
|
lfsr_file_truncate(&lfs, &a, 0) => 0;
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our truncate should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wwdrr_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, this should freeze its contents
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b, this may be tricky since we haven't touched b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a, close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wwdrr_append]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, write, and close
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen b, mark as desync, write
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// write a, sync
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// write b, close, desync flag should have been cleared
|
|
lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
|
|
strlen("hello!ohayo!annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
|
|
strlen("hello!ohayo!annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// write a, close
|
|
lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!"))
|
|
=> strlen("czesc!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!czesc!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!",
|
|
strlen("hello!ohayo!annyeong!czesc!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!czesc!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!",
|
|
strlen("hello!ohayo!annyeong!czesc!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test one desynced writer, multiple readers
|
|
[cases.test_fsync_drrr]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_drrr_fuzz]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test one writer, multiple desynced readers
|
|
[cases.test_fsync_wddd]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wddd_fuzz]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple desynced rd/wrers
|
|
[cases.test_fsync_rwdrwd]
|
|
defines.RW = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK;
|
|
memcpy(&between[rw][i], wbuf, CHUNK);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (i == 0) {
|
|
memcpy(after, between[rw], SIZE);
|
|
} else {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rwdrwd_fuzz]
|
|
defines.RW = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (i == 0) {
|
|
memcpy(after, between[rw], SIZE);
|
|
} else {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rwd files without fixed size
|
|
[cases.test_fsync_rwdrwd_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(100)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 3;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// broadcast sim
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// otherwise no change
|
|
} else if (sync == 2) {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rwd files while also truncating/fruncating
|
|
[cases.test_fsync_rwtfdrwtfd_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(100)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 4;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 3;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
|
|
// truncating?
|
|
} else if (op == 2) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// truncate
|
|
lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
size - between_size[rw]);
|
|
}
|
|
between_size[rw] = size;
|
|
|
|
// fruncating?
|
|
} else if (op == 3) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// fruncate
|
|
lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memmove(&between[rw][size - between_size[rw]],
|
|
between[rw],
|
|
between_size[rw]);
|
|
memset(between[rw],
|
|
0,
|
|
size - between_size[rw]);
|
|
} else {
|
|
memmove(between[rw],
|
|
&between[rw][between_size[rw] - size],
|
|
size);
|
|
}
|
|
between_size[rw] = size;
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// broadcast sim
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// resync?
|
|
} else if (sync == 2) {
|
|
lfsr_file_resync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// update sim
|
|
memcpy(between[rw], after, SIZE);
|
|
between_size[rw] = after_size;
|
|
|
|
// otherwise no change
|
|
} else if (sync == 2) {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Resyncing makes things even more interesting
|
|
|
|
# Some specific tests
|
|
[cases.test_fsync_resync_rwyrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// our write should show up in a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_read(&lfs, &a, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// resync a
|
|
lfsr_file_resync(&lfs, &a) => 0;
|
|
|
|
// a should have reverted to previous contents
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_read(&lfs, &a, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// our write should show up in a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_read(&lfs, &a, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_resync_wrrdy]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - desynced reader
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// but not in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// resync c, should now be up to date
|
|
lfsr_file_resync(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
|
|
// rewrite and sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and in c now that we resynced
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
|
|
// desync c, rewrite a and sync
|
|
lfsr_file_desync(&lfs, &c) => 0;
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// resync c, should now be up to date
|
|
lfsr_file_resync(&lfs, &c) => 0;
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
|
|
// desync c, rewrite a and close
|
|
lfsr_file_desync(&lfs, &c) => 0;
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// resync c, should now be up to date
|
|
lfsr_file_resync(&lfs, &c) => 0;
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_resync_wwdyrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, rewrite
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// resync b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// c should still show a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
// desync b
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// resync b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// rewrite b, close
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// b should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// c should still show b's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test that LFS_O_SYNC/LFS_O_FLUSH flags work as expected
|
|
[cases.test_fsync_resync_o_wdwyrr]
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
# SYNC=3 => sync via LFS_M_SYNC
|
|
defines.SYNC = [2, 3]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
# FLUSH=3 => flush via LFS_M_FLUSH
|
|
defines.FLUSH = [0, 2, 3]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs,
|
|
LFS_M_RDWR
|
|
| ((FLUSH == 3) ? LFS_M_FLUSH : 0)
|
|
| ((SYNC == 3) ? LFS_M_SYNC : 0),
|
|
CFG) => 0;
|
|
|
|
// a - desynced writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
// LFS_O_SYNC/LFS_M_SYNC should immediately create the file
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_CREAT
|
|
| LFS_O_EXCL
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)
|
|
| LFS_O_DESYNC) => 0;
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "jello", &info) => 0;
|
|
assert(strcmp(info.name, "jello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
// create the other files
|
|
lfsr_file_open(&lfs, &b, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to b
|
|
lfsr_file_write(&lfs, &b, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should revert to a's contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)
|
|
| LFS_O_DESYNC) => 0;
|
|
// rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// resync a
|
|
lfsr_file_resync(&lfs, &a) => 0;
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should still show b
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// desync a
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
// rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!r!", strlen("ohayo!r!"))
|
|
=> strlen("ohayo!r!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should revert to a's contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
// desync b
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// resync b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// should immediately show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!!");
|
|
assert(memcmp(rbuf, "zdrasti!!", strlen("zdrasti!!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!!");
|
|
assert(memcmp(rbuf, "zdrasti!!", strlen("zdrasti!!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close b
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// c should still show b's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!!");
|
|
assert(memcmp(rbuf, "zdrasti!!", strlen("zdrasti!!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!!");
|
|
assert(memcmp(rbuf, "zdrasti!!", strlen("zdrasti!!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_resync_wwdyrr_zero]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, rewrite
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
// truncate a, sync, this gets tricky as we no longer have a struct
|
|
lfsr_file_truncate(&lfs, &a, 0) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// resync b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// c should still show a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
// desync b
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// resync b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// truncate b, close
|
|
lfsr_file_truncate(&lfs, &b, 0) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// b should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// c should still show b's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_resync_wwdyrr_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, rewrite
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// resync b, this may be tricky since we haven't touched b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// c should still show a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
// desync b
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// resync b, close, this may be tricky since we haven't touched b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// b should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// c should still show b's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_resync_wwdyrr_append]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - desynced writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, rewrite
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// resync b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// c should still show a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
// desync b
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// resync b
|
|
lfsr_file_resync(&lfs, &b) => 0;
|
|
// rewrite b, close
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// b should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// c should still show b's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test one resyncing writer, multiple readers
|
|
[cases.test_fsync_yrrr]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
// resync
|
|
lfsr_file_resync(&lfs, &writer) => 0;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0 || SYNC == 1) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0 || SYNC == 1) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_yrrr_fuzz]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
// resync
|
|
lfsr_file_resync(&lfs, &writer) => 0;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0 || SYNC == 1) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0 || SYNC == 1) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test one writer, multiple resyncing readers
|
|
[cases.test_fsync_wyyy]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// resync desync readers
|
|
lfsr_file_resync(&lfs, &readers[r]) => 0;
|
|
lfsr_file_desync(&lfs, &readers[r]) => 0;
|
|
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wyyy_fuzz]
|
|
defines.R = 4
|
|
# SYNC=0 => no sync, readers not updated
|
|
# SYNC=1 => sync via lfsr_file_sync
|
|
# SYNC=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// resync desync readers
|
|
lfsr_file_resync(&lfs, &readers[r]) => 0;
|
|
lfsr_file_desync(&lfs, &readers[r]) => 0;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test multiple rwd files without fixed size
|
|
[cases.test_fsync_rwdyrwdy_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(100)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 4;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// broadcast sim
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// resync?
|
|
} else if (sync == 2) {
|
|
lfsr_file_resync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// update sim
|
|
memcpy(between[rw], after, SIZE);
|
|
between_size[rw] = after_size;
|
|
|
|
// otherwise no change
|
|
} else if (sync == 3) {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rwd files while also truncating/fruncating
|
|
[cases.test_fsync_rwtfdyrwtfdy_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
defines.SEED = 'range(100)'
|
|
fuzz = 'SEED'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 4;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 4;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
|
|
// truncating?
|
|
} else if (op == 2) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// truncate
|
|
lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
size - between_size[rw]);
|
|
}
|
|
between_size[rw] = size;
|
|
|
|
// fruncating?
|
|
} else if (op == 3) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// fruncate
|
|
lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memmove(&between[rw][size - between_size[rw]],
|
|
between[rw],
|
|
between_size[rw]);
|
|
memset(between[rw],
|
|
0,
|
|
size - between_size[rw]);
|
|
} else {
|
|
memmove(between[rw],
|
|
&between[rw][between_size[rw] - size],
|
|
size);
|
|
}
|
|
between_size[rw] = size;
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// broadcast sim
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// resync?
|
|
} else if (sync == 2) {
|
|
lfsr_file_resync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// update sim
|
|
memcpy(between[rw], after, SIZE);
|
|
between_size[rw] = after_size;
|
|
|
|
// otherwise no change
|
|
} else if (sync == 3) {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|