Files
littlefs/tests/test_fsync.toml
T
Christopher Haster 3ab7ecb2b0 Renamed file_cache -> fcache and gbmap_re -> regbmap
This walks back some of the attempt at strict object namespacing in
struct lfs3_cfg:

- cfg.file_cache_size  -> cfg.fcache_size
- filecfg.cache_size   -> filecfg.fcache_size
- filecfg.cache_buffer -> filecfg.fcache_buffer
- cfg.gbmap_re_thresh  -> cfg.regbmap_thresh

Motivation:

- cfg.regbmap_thresh now matches cfg.gc_regbmap_thresh, instead of using
  awkwardly different namespacing patterns.

- Giving fcache a more unique name is useful for discussion. Having
  pcache, rcache, and then file_cache was a bit awkward.

  Hopefully it's also more clear that cfg.fcache_size and
  filecfg.fcache_size are related.

- Config in struct lfs3_cfg is named a bit more consistently, well, if
  you ignore gc_*_* options.

- Less typing.

Though this gets into pretty subjective naming territory. May revert
this if the new terms are uncomfortable after use.
2025-10-24 00:18:54 -05:00

6177 lines
211 KiB
TOML

# Test multiple open file handles in different r/w configurations
after = 'test_fwrite'
# Some specific tests
[cases.test_fsync_sync_wrr]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - reader kept open, recvs updates from a
// c - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0;
// write to a
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
// should not show up in b yet
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => 0;
// or on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &c) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// now our write should show up in b
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// lets rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// b should still have previous contents
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// now our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// lets rewrite a one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// b should still have previous contents
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// close a
lfs3_file_close(&lfs3, &a) => 0;
// now our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
lfs3_file_close(&lfs3, &b) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected
[cases.test_fsync_sync_o_wsrr]
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// a - writer
// b - reader kept open, recvs updates from a
// c - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0;
// write to a
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
// should immediately show up in b
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should immediately show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// lets rewrite a one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// close a
lfs3_file_close(&lfs3, &a) => 0;
// should immediately show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
lfs3_file_close(&lfs3, &b) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_sync_wwrr]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - writer
// c - reader kept open, recvs updates from a/b
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and b
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should not show up in c yet
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// or on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// now our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// b's contents were clobbered, so we should see a
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// now our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite a one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// now our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected
[cases.test_fsync_sync_o_wswsrr]
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// a - synced writer
// b - synced writer
// c - reader kept open, recvs updates from a/b
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &b, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
// should immediately show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// write to b
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// a's contents were clobbered, so we should see b
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// a's contents were clobbered, so we should see b
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite a one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// should still have a
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test one non-LFS3_O_SYNC and one LFS3_O_SYNC
[cases.test_fsync_sync_o_wwsrr]
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// a - writer
// b - synced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0;
lfs3_file_open(&lfs3, &b, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
// write to b
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should immediately show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen a with LFS3_O_TRUNC
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_TRUNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen LFS3_O_WRONLY and rewrite a
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite a and b one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "moshimoshi!", strlen("moshimoshi!"))
=> strlen("moshimoshi!");
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!"))
=> strlen("czesc!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// should still have b
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_sync_wwrr_zero]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - writer
// c - reader kept open, recvs updates from a/b
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and b
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should not show up in c yet
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// or on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// now our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// truncate b, this gets tricky as we no longer have a struct
lfs3_file_truncate(&lfs3, &b, 0) => 0;
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// now our truncate should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// now our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// truncate a one last time
lfs3_file_truncate(&lfs3, &a, 0) => 0;
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// now our truncate should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_sync_wwrr_noop]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - writer
// c - reader kept open, recvs updates from a/b
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and b
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should not show up in c yet
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// or on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// now our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// b's contents were clobbered, so we should see a
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a, this may be tricky since we haven't touched b
lfs3_file_sync(&lfs3, &a) => 0;
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b, this may be tricky since we haven't touched b
lfs3_file_sync(&lfs3, &b) => 0;
// b's contents were clobbered, so we should see a
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite b one last time, this time with sync
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_sync(&lfs3, &b) => 0;
// now our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_sync_wwrr_append]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - writer
// c - reader kept open, recvs updates from a/b
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_APPEND) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and b
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should not show up in c yet
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// or on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// now our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// b's contents were clobbered, so we should see a
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// now our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite a one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// now our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!zdrasti!");
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
strlen("hello!ohayo!zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!zdrasti!");
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
strlen("hello!ohayo!zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# Test multiple readers, this shouldn't really have any issues
[cases.test_fsync_rrrr]
defines.R = 4
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// read R handles in parallel
lfs3_file_t readers[R];
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t r = 0; r < R; r++) {
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK;
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
}
}
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_rrrr_fuzz]
defines.R = 4
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = SEED;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// read R handles in parallel
lfs3_file_t readers[R];
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < N; i++) {
for (lfs3_size_t r = 0; r < R; r++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size;
assert(memcmp(rbuf, &before[off], size) == 0);
}
}
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# Test one writer, multiple readers
[cases.test_fsync_wrrr]
defines.R = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK;
memcpy(&after[i], wbuf, CHUNK);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
} else {
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_wrrr_fuzz]
defines.R = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < N; i++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, size) => size;
memcpy(&after[off], wbuf, size);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[off], size) == 0);
} else {
assert(memcmp(rbuf, &after[off], size) == 0);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# Test multiple writers
[cases.test_fsync_wwww]
defines.W = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write W handles in parallel
lfs3_file_t writers[W];
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_open(&lfs3, &writers[w], "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t w = 0; w < W; w++) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writers[w], wbuf, CHUNK) => CHUNK;
if (SYNC == 0) {
if (w == 0) {
memcpy(&after[i], wbuf, CHUNK);
}
} else {
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writers[w]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writers[w]) => 0;
}
memcpy(&after[i], wbuf, CHUNK);
}
}
}
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_close(&lfs3, &writers[w]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_wwww_fuzz]
defines.W = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write W files in parallel
lfs3_file_t writers[W];
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_open(&lfs3, &writers[w], "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t w = 0; w < W; w++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &writers[w], off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writers[w], wbuf, size) => size;
if (SYNC == 0) {
if (w == 0) {
memcpy(&after[off], wbuf, size);
}
} else {
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writers[w]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writers[w]) => 0;
}
memcpy(&after[off], wbuf, size);
}
}
}
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_close(&lfs3, &writers[w]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write W handles, R handles in parallel
lfs3_file_t writers[W];
lfs3_file_t readers[R];
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_open(&lfs3, &writers[w], "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t w = 0; w < W; w++) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writers[w], wbuf, CHUNK) => CHUNK;
if (SYNC == 0) {
if (w == 0) {
memcpy(&after[i], wbuf, CHUNK);
}
} else {
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writers[w]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writers[w]) => 0;
}
memcpy(&after[i], wbuf, CHUNK);
}
}
for (lfs3_size_t r = 0; r < R; r++) {
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
} else {
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
}
}
}
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_close(&lfs3, &writers[w]) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_wwrr_fuzz]
defines.W = 4
defines.R = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write W files in parallel
lfs3_file_t writers[W];
lfs3_file_t readers[R];
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_open(&lfs3, &writers[w], "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t w = 0; w < W; w++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &writers[w], off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writers[w], wbuf, size) => size;
if (SYNC == 0) {
if (w == 0) {
memcpy(&after[off], wbuf, size);
}
} else {
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writers[w]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writers[w]) => 0;
}
memcpy(&after[off], wbuf, size);
}
}
for (lfs3_size_t r = 0; r < R; r++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[off], size) == 0);
} else {
assert(memcmp(rbuf, &after[off], size) == 0);
}
}
}
for (lfs3_size_t w = 0; w < W; w++) {
lfs3_file_close(&lfs3, &writers[w]) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# Test multiple rd/wrers
[cases.test_fsync_rwrw]
defines.RW = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t between[RW][SIZE];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
memcpy(between[rw], before, SIZE);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t rw = 0; rw < RW; rw++) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i;
lfs3_file_write(&lfs3, &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) {
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
}
memcpy(&after[i], wbuf, CHUNK);
}
}
for (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &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 (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_rwrw_fuzz]
defines.RW = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t between[RW][SIZE];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
memcpy(between[rw], before, SIZE);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &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) {
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
}
memcpy(&after[off], wbuf, size);
}
}
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &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 (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.N = 40
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
uint32_t prng = 42;
uint8_t between[RW][SIZE];
lfs3_size_t between_size[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
between_size[rw] = 0;
}
uint8_t after[SIZE];
lfs3_size_t after_size = 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// open files
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_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
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
// write
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size;
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &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] = lfs3_max(off + size, between_size[rw]);
// reading?
} else if (op == 1) {
// choose a random offset
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off);
// read
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &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 (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) {
memcpy(between[rw_], between[rw], SIZE);
between_size[rw_] = between_size[rw];
}
}
}
}
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// close files
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
// broadcast sim one last time?
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size;
assert(memcmp(rbuf, after, after_size) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [0, 1, 2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 1, 2, 3]
defines.N = 40
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
uint32_t prng = 42;
uint8_t between[RW][SIZE];
lfs3_size_t between_size[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
between_size[rw] = 0;
}
uint8_t after[SIZE];
lfs3_size_t after_size = 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// open files
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// choose a random operation
uint8_t op = TEST_PRNG(&prng) % 4;
bool broadcast = false;
// writing?
if (op == 0) {
// choose a random offset
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
// write
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size;
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &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] = lfs3_max(off + size, between_size[rw]);
if (SYNC) {
broadcast = true;
}
// reading?
} else if (op == 1) {
// choose a random offset
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off);
// read
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size;
assert(memcmp(rbuf, &between[rw][off], size) == 0);
// truncating?
} else if (op == 2) {
// choose a random new file size
lfs3_off_t size = TEST_PRNG(&prng) % SIZE;
// truncate
lfs3_file_truncate(&lfs3, &rdwrs[rw], size) => 0;
if (FLUSH == 1) { // (flush does nothing)
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &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;
// note LFS3_O_SYNC does _not_ sync truncates
if (SYNC == 1) {
broadcast = true;
}
// fruncating?
} else if (op == 3) {
// choose a random new file size
lfs3_off_t size = TEST_PRNG(&prng) % SIZE;
// fruncate
lfs3_file_fruncate(&lfs3, &rdwrs[rw], size) => 0;
if (FLUSH == 1) { // (flush does nothing)
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &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;
// note LFS3_O_SYNC does _not_ sync fruncates
if (SYNC == 1) {
broadcast = true;
}
}
// broadcast sim?
if (broadcast) {
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) {
memcpy(between[rw_], between[rw], SIZE);
between_size[rw_] = between_size[rw];
}
}
}
}
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// close files
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
// broadcast sim one last time?
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size;
assert(memcmp(rbuf, after, after_size) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# Desynced files make things interesting
# Some specific tests
[cases.test_fsync_desync_wdrr]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - desynced writer
// b - reader kept open, recvs updates from a
// c - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// now mark as desync, rewrite, and close
lfs3_file_desync(&lfs3, &a) => 0;
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
lfs3_file_close(&lfs3, &a) => 0;
// b should still have previous contents
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// reopen a, mark as desync, rewrite a, and sync
lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_desync(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// this sync should clear the desync
lfs3_file_sync(&lfs3, &a) => 0;
// now our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// rewrite a, close, desync flag should have been cleared
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_close(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
lfs3_file_close(&lfs3, &b) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_desync_wrrd]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - reader kept open, recvs updates from a
// c - desynced reader
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// but not in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// reopen c, should now be up to date
lfs3_file_close(&lfs3, &c) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// rewrite and sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// but not in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// reopen c, should now be up to date
lfs3_file_close(&lfs3, &c) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// rewrite and close
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
lfs3_file_close(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// but not in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// reopen c, should now be up to date
lfs3_file_close(&lfs3, &c) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &b) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_desync_wwdrr]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, rewrite, and close
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
lfs3_file_close(&lfs3, &b) => 0;
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen b, mark as desync, rewrite
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// rewrite a, sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// b should show up in c, without a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b, close, desync flag should have been cleared
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
lfs3_file_close(&lfs3, &b) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a, close
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "czesc!", strlen("czesc!"))
=> strlen("czesc!");
lfs3_file_close(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected
[cases.test_fsync_desync_o_wsdwsrr]
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_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
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)
| LFS3_O_DESYNC) => 0;
lfs3_file_open(&lfs3, &b, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to b
lfs3_file_write(&lfs3, &b, "hello!", strlen("hello!"))
=> strlen("hello!");
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// should still have a
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen a
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)
| LFS3_O_DESYNC) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// desync a
lfs3_file_desync(&lfs3, &a) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite a one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "czesc!", strlen("czesc!"))
=> strlen("czesc!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// should still have a
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test one non-LFS3_O_SYNC and one LFS3_O_SYNC
[cases.test_fsync_desync_o_wdwsrr]
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// a - desynced writer
// b - synced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| LFS3_O_DESYNC) => 0;
lfs3_file_open(&lfs3, &b, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
// write to b
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should immediately show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen a with LFS3_O_TRUNC
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_TRUNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| LFS3_O_DESYNC) => 0;
// c should still have a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// reopen LFS3_O_WRONLY and rewrite a
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// c should still have a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// lets rewrite a and b one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "moshimoshi!", strlen("moshimoshi!"))
=> strlen("moshimoshi!");
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!"))
=> strlen("czesc!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// should still have b
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!");
assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_desync_wwdrr_zero]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, truncate, and close, this gets tricky as we no
// longer have a struct
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_truncate(&lfs3, &b, 0) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen b, mark as desync, truncate
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_truncate(&lfs3, &b, 0) => 0;
// rewrite a, sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// b should show up in c, without a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b, close, desync flag should have been cleared
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
lfs3_file_close(&lfs3, &b) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// truncate a, close
lfs3_file_truncate(&lfs3, &a, 0) => 0;
lfs3_file_close(&lfs3, &a) => 0;
// our truncate should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_desync_wwdrr_noop]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, this should freeze its contents
lfs3_file_desync(&lfs3, &b) => 0;
// rewrite a, sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b, this may be tricky since we haven't touched b
lfs3_file_sync(&lfs3, &b) => 0;
// b should show up in c, without a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b, close, desync flag should have been cleared
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
lfs3_file_close(&lfs3, &b) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a, close
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_close(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_desync_wwdrr_append]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_APPEND) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, write, and close
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
lfs3_file_close(&lfs3, &b) => 0;
// c should still have previous contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen b, mark as desync, write
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0;
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// write a, sync
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// b should show up in c, without a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// write b, close, desync flag should have been cleared
lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
lfs3_file_close(&lfs3, &b) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!annyeong!");
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
strlen("hello!ohayo!annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!annyeong!");
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
strlen("hello!ohayo!annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// write a, close
lfs3_file_write(&lfs3, &a, "czesc!", strlen("czesc!"))
=> strlen("czesc!");
lfs3_file_close(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &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
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!annyeong!czesc!");
assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!",
strlen("hello!ohayo!annyeong!czesc!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| LFS3_O_DESYNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK;
memcpy(&after[i], wbuf, CHUNK);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
} else {
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
if (SYNC == 0) {
assert(memcmp(rbuf, before, SIZE) == 0);
} else {
assert(memcmp(rbuf, after, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_drrr_fuzz]
defines.R = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| LFS3_O_DESYNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < N; i++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, size) => size;
memcpy(&after[off], wbuf, size);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[off], size) == 0);
} else {
assert(memcmp(rbuf, &after[off], size) == 0);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
if (SYNC == 0) {
assert(memcmp(rbuf, before, SIZE) == 0);
} else {
assert(memcmp(rbuf, after, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello",
LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK;
memcpy(&after[i], wbuf, CHUNK);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK;
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_wddd_fuzz]
defines.R = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello",
LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
}
for (lfs3_size_t i = 0; i < N; i++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, size) => size;
memcpy(&after[off], wbuf, size);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size;
assert(memcmp(rbuf, &before[off], size) == 0);
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# Test multiple desynced rd/wrers
[cases.test_fsync_rwdrwd]
defines.RW = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t between[RW][SIZE];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
memcpy(between[rw], before, SIZE);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_DESYNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t rw = 0; rw < RW; rw++) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i;
lfs3_file_write(&lfs3, &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) {
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
}
if (i == 0) {
memcpy(after, between[rw], SIZE);
} else {
memcpy(&after[i], wbuf, CHUNK);
}
}
}
for (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &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 (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
if (SYNC == 0) {
assert(memcmp(rbuf, before, SIZE) == 0);
} else {
assert(memcmp(rbuf, after, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_rwdrwd_fuzz]
defines.RW = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t between[RW][SIZE];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
memcpy(between[rw], before, SIZE);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_DESYNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &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) {
lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
}
if (i == 0) {
memcpy(after, between[rw], SIZE);
} else {
memcpy(&after[off], wbuf, size);
}
}
}
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &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 (lfs3_size_t rw = 0; rw < RW; rw++) {
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
if (SYNC == 0) {
assert(memcmp(rbuf, before, SIZE) == 0);
} else {
assert(memcmp(rbuf, after, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 40
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
uint8_t between[RW][SIZE];
lfs3_size_t between_size[RW];
bool between_desync[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
between_size[rw] = 0;
between_desync[rw] = false;
}
uint8_t after[SIZE];
lfs3_size_t after_size = 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// open files
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_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
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
// write
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size;
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &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] = lfs3_max(off + size, between_size[rw]);
// reading?
} else if (op == 1) {
// choose a random offset
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off);
// read
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size;
assert(memcmp(rbuf, &between[rw][off], size) == 0);
}
// desync?
if (sync == 0) {
lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = true;
// sync?
} else if (sync == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = false;
// broadcast sim
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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 (lfs3_size_t rw = 0; rw < RW; rw++) {
// close files
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
// broadcast sim one last time?
if (!between_desync[rw]) {
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size;
assert(memcmp(rbuf, after, after_size) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 40
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
uint8_t between[RW][SIZE];
lfs3_size_t between_size[RW];
bool between_desync[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
between_size[rw] = 0;
between_desync[rw] = false;
}
uint8_t after[SIZE];
lfs3_size_t after_size = 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// open files
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_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
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
// write
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size;
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &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] = lfs3_max(off + size, between_size[rw]);
// reading?
} else if (op == 1) {
// choose a random offset
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off);
// read
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size;
assert(memcmp(rbuf, &between[rw][off], size) == 0);
// truncating?
} else if (op == 2) {
// choose a random new file size
lfs3_off_t size = TEST_PRNG(&prng) % SIZE;
// truncate
lfs3_file_truncate(&lfs3, &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
lfs3_off_t size = TEST_PRNG(&prng) % SIZE;
// fruncate
lfs3_file_fruncate(&lfs3, &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) {
lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = true;
// sync?
} else if (sync == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = false;
// broadcast sim
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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) {
lfs3_file_resync(&lfs3, &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 (lfs3_size_t rw = 0; rw < RW; rw++) {
// close files
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
// broadcast sim one last time?
if (!between_desync[rw]) {
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size;
assert(memcmp(rbuf, after, after_size) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# Resyncing makes things even more interesting
# Some specific tests
[cases.test_fsync_resync_rwyrr]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - reader kept open, recvs updates from a
// c - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// our write should show up in a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_read(&lfs3, &a, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// b should still have previous contents
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// resync a
lfs3_file_resync(&lfs3, &a) => 0;
// a should have reverted to previous contents
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_read(&lfs3, &a, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// b should still have previous contents
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// our write should show up in a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_read(&lfs3, &a, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// b should still have previous contents
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
// close a
lfs3_file_close(&lfs3, &a) => 0;
// now our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &c) => 0;
lfs3_file_close(&lfs3, &b) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_resync_wrrdy]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - reader kept open, recvs updates from a
// c - desynced reader
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// but not in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// resync c, should now be up to date
lfs3_file_resync(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// rewrite and sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and in c now that we resynced
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// desync c, rewrite a and sync
lfs3_file_desync(&lfs3, &c) => 0;
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// but not in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// resync c, should now be up to date
lfs3_file_resync(&lfs3, &c) => 0;
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// desync c, rewrite a and close
lfs3_file_desync(&lfs3, &c) => 0;
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_close(&lfs3, &a) => 0;
// our write should show up in b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// but not in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// resync c, should now be up to date
lfs3_file_resync(&lfs3, &c) => 0;
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &b) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_resync_wwdyrr]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, rewrite
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// rewrite a, sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// resync b
lfs3_file_resync(&lfs3, &b) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// c should still show a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// desync b
lfs3_file_desync(&lfs3, &b) => 0;
// resync b
lfs3_file_resync(&lfs3, &b) => 0;
// rewrite b, close
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_close(&lfs3, &b) => 0;
// b should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a
lfs3_file_close(&lfs3, &a) => 0;
// c should still show b's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected
[cases.test_fsync_resync_o_wsdywsrr]
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [2, 3]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// a - desynced synced writer
// b - synced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)
| LFS3_O_DESYNC) => 0;
lfs3_file_open(&lfs3, &b, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to b
lfs3_file_write(&lfs3, &b, "hello!", strlen("hello!"))
=> strlen("hello!");
// should immediately show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// reopen a
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)
| LFS3_O_DESYNC) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// reopen a
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)
| LFS3_O_DESYNC) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// resync a
lfs3_file_resync(&lfs3, &a) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should still show b's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// desync a
lfs3_file_desync(&lfs3, &a) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should revert to a's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// desync b
lfs3_file_desync(&lfs3, &b) => 0;
// resync b
lfs3_file_resync(&lfs3, &b) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!"))
=> strlen("czesc!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close b
lfs3_file_close(&lfs3, &b) => 0;
// close a
lfs3_file_close(&lfs3, &a) => 0;
// c should still show b's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test one non-LFS3_O_SYNC and one LFS3_O_SYNC
[cases.test_fsync_resync_o_wdywsrr]
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
# SYNC=3 => sync via LFS3_M_SYNC
defines.SYNC = [2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
# FLUSH=3 => flush via LFS3_M_FLUSH
defines.FLUSH = [0, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
| ((FLUSH == 3) ? LFS3_M_FLUSH : 0)
| ((SYNC == 3) ? LFS3_M_SYNC : 0),
CFG) => 0;
// a - desynced writer
// b - synced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| LFS3_O_DESYNC) => 0;
lfs3_file_open(&lfs3, &b, "jello",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
// write to b
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// should immediately show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// resync a
lfs3_file_resync(&lfs3, &a) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen a with LFS3_O_TRUNC
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| LFS3_O_TRUNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| LFS3_O_DESYNC) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// resync a
lfs3_file_resync(&lfs3, &a) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// reopen LFS3_O_WRONLY and rewrite a
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite b
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// resync a
lfs3_file_resync(&lfs3, &a) => 0;
// sync a
lfs3_file_sync(&lfs3, &a) => 0;
// c should still have b's contents
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// desync a
lfs3_file_desync(&lfs3, &a) => 0;
// lets rewrite a and b one last time
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "moshimoshi!", strlen("moshimoshi!"))
=> strlen("moshimoshi!");
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!"))
=> strlen("czesc!");
// resync a
lfs3_file_resync(&lfs3, &a) => 0;
// should immediately show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a and b
lfs3_file_close(&lfs3, &a) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// should still have b
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_resync_wwdyrr_zero]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, rewrite
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// truncate a, sync, this gets tricky as we no longer have a struct
lfs3_file_truncate(&lfs3, &a, 0) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// resync b
lfs3_file_resync(&lfs3, &b) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// c should still show a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// desync b
lfs3_file_desync(&lfs3, &b) => 0;
// resync b
lfs3_file_resync(&lfs3, &b) => 0;
// truncate b, close
lfs3_file_truncate(&lfs3, &b, 0) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// b should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
// close a
lfs3_file_close(&lfs3, &a) => 0;
// c should still show b's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0;
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0;
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_resync_wwdyrr_noop]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, rewrite
lfs3_file_desync(&lfs3, &b) => 0;
// rewrite a, sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// resync b, this may be tricky since we haven't touched b
lfs3_file_resync(&lfs3, &b) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// c should still show a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// desync b
lfs3_file_desync(&lfs3, &b) => 0;
// resync b, close, this may be tricky since we haven't touched b
lfs3_file_resync(&lfs3, &b) => 0;
lfs3_file_close(&lfs3, &b) => 0;
// b should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a
lfs3_file_close(&lfs3, &a) => 0;
// c should still show b's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_resync_wwdyrr_append]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// a - writer
// b - desynced writer
// c - reader kept open, recvs updates from a
// d - reader kept closed, checks disk state
lfs3_file_t a;
lfs3_file_t b;
lfs3_file_t c;
lfs3_file_t d;
uint8_t rbuf[256];
lfs3_file_open(&lfs3, &a, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_APPEND) => 0;
lfs3_file_sync(&lfs3, &a) => 0;
lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0;
lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0;
// write to a and sync
lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!"))
=> strlen("hello!");
lfs3_file_sync(&lfs3, &a) => 0;
// our write should show up in c
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// mark b as desync, rewrite
lfs3_file_desync(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!"))
=> strlen("bonjour!");
// rewrite a, sync
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!"))
=> strlen("ohayo!");
lfs3_file_sync(&lfs3, &a) => 0;
// a should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// resync b
lfs3_file_resync(&lfs3, &b) => 0;
// sync b
lfs3_file_sync(&lfs3, &b) => 0;
// c should still show a's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// rewrite a
lfs3_file_rewind(&lfs3, &a) => 0;
lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!"))
=> strlen("annyeong!");
// desync b
lfs3_file_desync(&lfs3, &b) => 0;
// resync b
lfs3_file_resync(&lfs3, &b) => 0;
// rewrite b, close
lfs3_file_rewind(&lfs3, &b) => 0;
lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!"))
=> strlen("zdrasti!");
lfs3_file_close(&lfs3, &b) => 0;
// b should show up in c
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!zdrasti!");
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
strlen("hello!ohayo!zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!zdrasti!");
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
strlen("hello!ohayo!zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
// close a
lfs3_file_close(&lfs3, &a) => 0;
// c should still show b's changes
lfs3_file_rewind(&lfs3, &c) => 0;
lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!zdrasti!");
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
strlen("hello!ohayo!zdrasti!")) == 0);
// and on disk
lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf))
=> strlen("hello!ohayo!zdrasti!");
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
strlen("hello!ohayo!zdrasti!")) == 0);
lfs3_file_close(&lfs3, &d) => 0;
lfs3_file_close(&lfs3, &c) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| LFS3_O_DESYNC
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK;
// resync
lfs3_file_resync(&lfs3, &writer) => 0;
memcpy(&after[i], wbuf, CHUNK);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &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);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
if (SYNC == 0 || SYNC == 1) {
assert(memcmp(rbuf, before, SIZE) == 0);
} else {
assert(memcmp(rbuf, after, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_yrrr_fuzz]
defines.R = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0;
}
for (lfs3_size_t i = 0; i < N; i++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, size) => size;
// resync
lfs3_file_resync(&lfs3, &writer) => 0;
memcpy(&after[off], wbuf, size);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &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);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
if (SYNC == 0 || SYNC == 1) {
assert(memcmp(rbuf, before, SIZE) == 0);
} else {
assert(memcmp(rbuf, after, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = '(SIZE+16-1) / 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello",
LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK;
memcpy(&after[i], wbuf, CHUNK);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
// resync desync readers
lfs3_file_resync(&lfs3, &readers[r]) => 0;
lfs3_file_desync(&lfs3, &readers[r]) => 0;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
} else {
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_fsync_wyyy_fuzz]
defines.R = 4
# SYNC=0 => no sync, readers not updated
# SYNC=1 => sync via lfs3_file_sync
# SYNC=2 => sync via LFS3_O_SYNC
defines.SYNC = [0, 1, 2]
# FLUSH=0 => no flush
# FLUSH=1 => flush via lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 20
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
uint32_t prng = 42;
uint8_t before[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t after[SIZE];
memcpy(after, before, SIZE);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// write 1 handle, read R handles in parallel
lfs3_file_t writer;
lfs3_file_t readers[R];
lfs3_file_open(&lfs3, &writer, "jello",
LFS3_O_WRONLY
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)
| ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_open(&lfs3, &readers[r], "jello",
LFS3_O_RDONLY | LFS3_O_DESYNC) => 0;
}
for (lfs3_size_t i = 0; i < N; i++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off;
// write
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &writer, wbuf, size) => size;
memcpy(&after[off], wbuf, size);
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &writer) => 0;
}
if (SYNC == 1) {
lfs3_file_sync(&lfs3, &writer) => 0;
}
for (lfs3_size_t r = 0; r < R; r++) {
// choose a random offset
lfs3_off_t off = TEST_PRNG(&prng) % SIZE;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off;
// resync desync readers
lfs3_file_resync(&lfs3, &readers[r]) => 0;
lfs3_file_desync(&lfs3, &readers[r]) => 0;
// read
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size;
if (SYNC == 0) {
assert(memcmp(rbuf, &before[off], size) == 0);
} else {
assert(memcmp(rbuf, &after[off], size) == 0);
}
}
}
lfs3_file_close(&lfs3, &writer) => 0;
for (lfs3_size_t r = 0; r < R; r++) {
lfs3_file_close(&lfs3, &readers[r]) => 0;
}
// check that file was written as expected
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, after, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 40
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
uint8_t between[RW][SIZE];
lfs3_size_t between_size[RW];
bool between_desync[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
between_size[rw] = 0;
between_desync[rw] = false;
}
uint8_t after[SIZE];
lfs3_size_t after_size = 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// open files
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_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
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
// write
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size;
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &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] = lfs3_max(off + size, between_size[rw]);
// reading?
} else if (op == 1) {
// choose a random offset
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off);
// read
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size;
assert(memcmp(rbuf, &between[rw][off], size) == 0);
}
// desync?
if (sync == 0) {
lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = true;
// sync?
} else if (sync == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = false;
// broadcast sim
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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) {
lfs3_file_resync(&lfs3, &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 (lfs3_size_t rw = 0; rw < RW; rw++) {
// close files
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
// broadcast sim one last time?
if (!between_desync[rw]) {
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size;
assert(memcmp(rbuf, after, after_size) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 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 lfs3_file_flush
# FLUSH=2 => flush via LFS3_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.N = 40
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
uint8_t between[RW][SIZE];
lfs3_size_t between_size[RW];
bool between_desync[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
between_size[rw] = 0;
between_desync[rw] = false;
}
uint8_t after[SIZE];
lfs3_size_t after_size = 0;
// write RW rdwrs in parallel
lfs3_file_t rdwrs[RW];
for (lfs3_size_t rw = 0; rw < RW; rw++) {
// open files
lfs3_file_open(&lfs3, &rdwrs[rw], "jello",
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0;
}
for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) {
for (lfs3_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
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, SIZE - off);
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < size; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
// write
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size;
if (FLUSH == 1) {
lfs3_file_flush(&lfs3, &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] = lfs3_max(off + size, between_size[rw]);
// reading?
} else if (op == 1) {
// choose a random offset
lfs3_off_t off = (between_size[rw] > 0)
? TEST_PRNG(&prng) % between_size[rw]
: 0;
lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off);
// read
lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size;
assert(memcmp(rbuf, &between[rw][off], size) == 0);
// truncating?
} else if (op == 2) {
// choose a random new file size
lfs3_off_t size = TEST_PRNG(&prng) % SIZE;
// truncate
lfs3_file_truncate(&lfs3, &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
lfs3_off_t size = TEST_PRNG(&prng) % SIZE;
// fruncate
lfs3_file_fruncate(&lfs3, &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) {
lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = true;
// sync?
} else if (sync == 1) {
lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0;
between_desync[rw] = false;
// broadcast sim
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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) {
lfs3_file_resync(&lfs3, &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 (lfs3_size_t rw = 0; rw < RW; rw++) {
// close files
lfs3_file_close(&lfs3, &rdwrs[rw]) => 0;
// broadcast sim one last time?
if (!between_desync[rw]) {
memcpy(after, between[rw], SIZE);
after_size = between_size[rw];
for (lfs3_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
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size;
assert(memcmp(rbuf, after, after_size) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''