Files
littlefs/tests/test_stickynotes.toml
T
Christopher Haster bc9562c64e tests: Accidentally found a couple buffer overruns
- Off-by-one in test_btree_find_general[_sparse]_fuzz

  Because we can only create named btrees via splitting, these always
  start with one entry. If all operations are randomly selected to be
  splits, this can lead to an overflow of the sim buffer (sounds
  unlikely, but relatively easily for small N).

  The fix is to use a `for (lfs3_size_t i = 1; i < N; i++)` loop to
  account for the initial entry. Note we already use this in the
  test_btree_split_* tests.

  An alternative is allocating space for N+1 entries, but this seems
  unintuitive with N usually being associated with the upper bound on
  btree size.

- Off-by-one in our sim rename pattern

  When renaming, we don't bother to update sim_size, because after the
  rename the sim_size size will be unchanged. But this means the
  sim_size is out-of-date during the memmove that reinserts the renamed
  entry. Buffer overflow!

  To fix we just need to use sim_size-1 to account for the temporarily
  deleted entry.

  This is messy C code, so not surprised it went unnoticed, even though
  this pattern ended up in quite a few tests.

Found while running with HEAP=1. This was just intended to test HEAP=1,
but I guess the injected heap hooks result in a more fragile heap? They
increase all allocations by one word, and maybe this reduces alignment
padding? Not exactly sure.

But it's a good argument for maybe adding heap canaries in the future.
Previously we ran Valgrind on all tests, but it's unclear if this will
still be reasonable with the number of tests we have now.
2026-02-19 12:39:20 -06:00

11767 lines
388 KiB
TOML

# Test orphaned files and their various use cases
after = ['test_fwrite', 'test_fsync']
# test files don't exist until first sync/close
[cases.test_stickynotes_uncreat]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// check that the file doesn't _really_ exist
lfs3_t lfs3_;
lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0;
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
lfs3_unmount(&lfs3_) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// check that the file still doesn't _really_ exist
lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_,
"batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
lfs3_unmount(&lfs3_) => 0;
}
if (SYNC) {
// sync the file
lfs3_file_sync(&lfs3, &file) => 0;
// now it should show up
lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3_, &file_) => 0;
lfs3_unmount(&lfs3_) => 0;
}
// close the file
lfs3_file_close(&lfs3, &file) => 0;
// now it should show up
lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3_, &file_) => 0;
lfs3_unmount(&lfs3_) => 0;
lfs3_unmount(&lfs3) => 0;
// should still be there
lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3_, &file_) => 0;
lfs3_unmount(&lfs3_) => 0;
'''
# test files don't exist until first sync/close, even under powerloss
[cases.test_stickynotes_uncreat_pl]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
reentrant = true
code = '''
// format once per test
lfs3_t lfs3;
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
if (err) {
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// create a file
//
// note the excl flag
lfs3_file_t file;
err = lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL);
assert(!err || err == LFS3_ERR_EXIST);
if (err != LFS3_ERR_EXIST) {
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// close the file
lfs3_file_close(&lfs3, &file) => 0;
}
// we should be able to read the file now
lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0;
uint32_t prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
// and after remounting
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test files don't exist until first sync/close, even under powerloss
[cases.test_stickynotes_uncreat_many_pl]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
]
defines.CHUNK = 8
defines.N = 128
reentrant = true
code = '''
// format once per test
lfs3_t lfs3;
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
if (err) {
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// create N files
for (lfs3_size_t j = 0; j < N; j++) {
// note the excl flag
char name[256];
sprintf(name, "batman%03x", j);
lfs3_file_t file;
err = lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL);
assert(!err || err == LFS3_ERR_EXIST);
if (err != LFS3_ERR_EXIST) {
// write to the file
uint32_t prng = 42 + j;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// close the file
lfs3_file_close(&lfs3, &file) => 0;
}
}
// we should be able to read the files now
for (lfs3_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
}
// and after remounting
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
for (lfs3_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test files only exist as stickynotes until first sync/close
[cases.test_stickynotes_uncreat_sync_wr]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
// but we should still recieve sync broadcasts on sync/close
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
if (SYNC) {
// sync the file
lfs3_file_sync(&lfs3, &file) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the file
lfs3_file_close(&lfs3, &file) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file__) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_sync_rw]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// open a second reference
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// write to the second file
uint32_t prng = 42;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the second file
lfs3_file_close(&lfs3, &file__) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_wdwr]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desync file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
// open a second reference
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// and a third for checking sync broadcasts
lfs3_file_t file___;
lfs3_file_open(&lfs3, &file___, "batman",
LFS3_O_RDWR | LFS3_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// write to the first file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// now it should show up
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the second file
lfs3_file_close(&lfs3, &file__) => 0;
// now it should show up
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// now sync the first file, this should overwrite what is written
lfs3_file_sync(&lfs3, &file) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// and close, this shouldn't change anything
//
// note we must sync to clear the desync flag
lfs3_file_close(&lfs3, &file) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file___) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_open]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new file over the uncreat
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
// our uncreat should have been overwritten
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_excl]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// attempt to create a new file over the uncreat
//
// this errors, otherwise it's easy to create the same file twice
// with excl, which would be super duper confusing for users
//
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => LFS3_ERR_EXIST;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the excl file had no effect
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_mkdir]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// attempt to mkdir
//
// this should fail because of the stickynote
//
lfs3_mkdir(&lfs3, "batman") => LFS3_ERR_EXIST;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the excl file had no effect
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test we can remove stickynotes
[cases.test_stickynotes_uncreat_rm]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// remove the stickynote
lfs3_remove(&lfs3, "batman") => 0;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// the stickynote should be gone
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test we can rename stickynotes
[cases.test_stickynotes_uncreat_mv]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename the stickynote
lfs3_rename(&lfs3, "batman", "catman") => 0;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test we can rename stickynotes onto files
[cases.test_stickynotes_uncreat_mv_replace]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreate
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create another file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "catman",
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename the stickynote
lfs3_rename(&lfs3, "batman", "catman") => 0;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test we can rename stickynotes onto other files
[cases.test_stickynotes_uncreat_mv_src_file]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreate
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "catman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename the stickynote
lfs3_rename(&lfs3, "batman", "catman") => 0;
// we should still be able to read our uncreate/file
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
lfs3_file_rewind(&lfs3, &file) => 0;
lfs3_file_read(&lfs3, &file, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test we can rename files onto stickynotes
[cases.test_stickynotes_uncreat_mv_dst_file]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreate
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "catman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename onto the stickynote
lfs3_rename(&lfs3, "catman", "batman") => 0;
// we should still be able to read our uncreate/file
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
lfs3_file_rewind(&lfs3, &file) => 0;
lfs3_file_read(&lfs3, &file, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// test that the file replaced the stickynote
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test we can rename stickynotes onto other stickynotes
[cases.test_stickynotes_uncreat_mv_stickynote]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreate
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create another uncreate
lfs3_file_t uncreat_;
lfs3_file_open(&lfs3, &uncreat_, "catman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat_, "WoO~", strlen("WoO~"))
=> strlen("WoO~");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename the stickynote
lfs3_rename(&lfs3, "batman", "catman") => 0;
// we should still be able to read our uncreates
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
lfs3_file_rewind(&lfs3, &uncreat_) => 0;
lfs3_file_read(&lfs3, &uncreat_, rbuf, sizeof(rbuf))
=> strlen("WoO~");
assert(memcmp(rbuf, "WoO~", strlen("WoO~")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
lfs3_file_close(&lfs3, &uncreat_) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
lfs3_file_close(&lfs3, &uncreat_) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test renaming a stickynote onto itself does nothing
[cases.test_stickynotes_uncreat_mv_noop]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreate
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename the stickynote
lfs3_rename(&lfs3, "batman", "batman") => 0;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_not_dir]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// attempt to rename a dir onto the stickynote
//
// this should fail because of the stickynote
//
lfs3_mkdir(&lfs3, "datman") => 0;
lfs3_rename(&lfs3, "datman", "batman") => LFS3_ERR_NOTDIR;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_stat(&lfs3, "datman", &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_not_stickynote]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// attempt to rename the stickynote onto a dir
//
// this should fail because of the stickynote
//
lfs3_mkdir(&lfs3, "datman") => 0;
lfs3_rename(&lfs3, "batman", "datman") => LFS3_ERR_ISDIR;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_stat(&lfs3, "datman", &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_not_root]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// attempt to rename the stickynote onto the root
//
// this should fail because of the stickynote
//
// well, because of the root really, but also because of the
// stickynote
//
lfs3_rename(&lfs3, "batman", "/") => LFS3_ERR_BUSY;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_uncreat_not_noent]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// attempt to rename the stickynote onto an invalid path
//
// this should fail because of the stickynote
//
// well, because of the invalid path really, but also because of the
// stickynote
//
lfs3_rename(&lfs3, "batman", "no/batman") => LFS3_ERR_NOENT;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
if (CLOSE == 1) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test desync files don't exist until first sync + close
[cases.test_stickynotes_undesync]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desync file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// check that the file doesn't _really_ exist
lfs3_t lfs3_;
lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0;
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
lfs3_unmount(&lfs3_) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// check that the file still doesn't _really_ exist
lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_,
"batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
lfs3_unmount(&lfs3_) => 0;
}
if (SYNC) {
// sync the file
lfs3_file_sync(&lfs3, &file) => 0;
// now it should show up
lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3_, &file_) => 0;
lfs3_unmount(&lfs3_) => 0;
}
// sync the file
lfs3_file_sync(&lfs3, &file) => 0;
// close the file
lfs3_file_close(&lfs3, &file) => 0;
// now it should show up
lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3_, &file_) => 0;
lfs3_unmount(&lfs3_) => 0;
lfs3_unmount(&lfs3) => 0;
// should still be there
lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0;
// via stat
lfs3_stat(&lfs3_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3_, &dir, "/") => 0;
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3_, &dir) => 0;
// via open
lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3_, &file_) => 0;
lfs3_unmount(&lfs3_) => 0;
'''
# test desync files don't exist until first sync + close, even under powerloss
[cases.test_stickynotes_undesync_pl]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
reentrant = true
code = '''
// format once per test
lfs3_t lfs3;
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
if (err) {
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// create a desync file
//
// note the excl flag
lfs3_file_t file;
err = lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC);
assert(!err || err == LFS3_ERR_EXIST);
if (err != LFS3_ERR_EXIST) {
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// sync the file
lfs3_file_sync(&lfs3, &file) => 0;
// close the file
lfs3_file_close(&lfs3, &file) => 0;
}
// we should be able to read the file now
lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0;
uint32_t prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
// and after remounting
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test desync files don't exist until first sync + close, even under powerloss
[cases.test_stickynotes_undesync_many_pl]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
]
defines.CHUNK = 8
defines.N = 128
reentrant = true
code = '''
// format once per test
lfs3_t lfs3;
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
if (err) {
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// create N desync files
lfs3_file_t files[N];
bool exists[N];
for (lfs3_size_t j = 0; j < N; j++) {
// note the excl flag
char name[256];
sprintf(name, "batman%03x", j);
err = lfs3_file_open(&lfs3, &files[j], name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC);
assert(!err || err == LFS3_ERR_EXIST);
exists[j] = (err == LFS3_ERR_EXIST);
if (!exists[j]) {
// write to the file
uint32_t prng = 42 + j;
for (lfs3_off_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, &files[j], wbuf, CHUNK) => CHUNK;
}
}
}
// sync and close
for (lfs3_size_t j = 0; j < N; j++) {
if (!exists[j]) {
// sync the file
lfs3_file_sync(&lfs3, &files[j]) => 0;
// close the file
lfs3_file_close(&lfs3, &files[j]) => 0;
}
}
// we should be able to read the files now
for (lfs3_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
}
// and after remounting
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
for (lfs3_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test desync files aren't even openable until first sync + close
[cases.test_stickynotes_undesync_sync_wr]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desync file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_,
"batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_,
"batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
}
// but we should still recieve sync broadcasts on sync + close
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
if (SYNC) {
// sync the file
lfs3_file_sync(&lfs3, &file) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// sync the file
lfs3_file_sync(&lfs3, &file) => 0;
// close the file
lfs3_file_close(&lfs3, &file) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file__) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_undesync_sync_rw]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// open a second desync reference
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// write to the second file
uint32_t prng = 42;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// close the second file
lfs3_file_close(&lfs3, &file__) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_undesync_wdwr]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desync file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
// open a second desync reference
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
// and a third for checking sync broadcasts
lfs3_file_t file___;
lfs3_file_open(&lfs3, &file___, "batman",
LFS3_O_RDWR | LFS3_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// write to the first file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// now it should show up
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// close the second file
lfs3_file_close(&lfs3, &file__) => 0;
// now it should show up
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// now sync the first file, this should overwrite what is written
lfs3_file_sync(&lfs3, &file) => 0;
// now it should show up
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// and close, this shouldn't change anything
//
// note we must sync to clear the desync flag
lfs3_file_close(&lfs3, &file) => 0;
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file___) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_undesync_open]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desynced uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new file over the uncreat
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_undesync_excl]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
defines.CLOSE = [0, 1]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
defines.REMOUNT = [0, 1]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desynced uncreat
lfs3_file_t uncreat;
lfs3_file_open(&lfs3, &uncreat, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new file over the uncreat
//
// while it's still possible for the desynced uncreat to create the
// file by explicitly calling lfs3_file_sync, for the most part we
// treat desynced files like zombies and allow excl creates
//
// this makes lfs3_file_sync/resync roughly the same as opening the
// file after the excl create succeeds, and if you're using desynced
// files you should probably be aware of littlefs's snapshot model
// anyways
//
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
// we should still be able to read our uncreat
lfs3_file_rewind(&lfs3, &uncreat) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &uncreat) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that desynced, and then closed, files don't show up
[cases.test_stickynotes_orphan]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desync file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_,
"batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_,
"batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
}
// close
lfs3_file_close(&lfs3, &file) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// because the file was desynced, it should still not exist
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// even after a remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// because the file was desynced, it should still not exist
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_wdwr]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
# SYNC=0x1 => sync before orphaning
# SYNC=0x2 => sync after orphaning
defines.SYNC = [0, 1, 2, 3]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desync file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
// open a second reference
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// and a third for checking sync broadcasts
lfs3_file_t file___;
lfs3_file_open(&lfs3, &file___, "batman",
LFS3_O_RDWR | LFS3_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// write to the first file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
}
if (SYNC & 0x1) {
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// now it should show up
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the first file, this should do nothing but discard the
// file contents
lfs3_file_close(&lfs3, &file) => 0;
if (SYNC & 0x2) {
// sync the second file
lfs3_file_sync(&lfs3, &file__) => 0;
// now it should show up
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the second file
lfs3_file_close(&lfs3, &file__) => 0;
// now it should show up
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// recieved sync broadcast?
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file___) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_unrelated]
# different number of orphan require different methods of cleanup
defines.N = 'range(6)'
defines.M = 'range(6)'
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create N orphans
lfs3_file_t orphans[N];
for (lfs3_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
lfs3_file_open(&lfs3, &orphans[o], name,
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
uint32_t prng = 42+o;
for (lfs3_off_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, &orphans[o], wbuf, CHUNK) => CHUNK;
}
}
// and an unrelated file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
uint32_t prng = 52;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// as far as the filesystem is concerned, none of the orphans exist
// via stat
struct lfs3_info info;
for (lfs3_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
lfs3_stat(&lfs3, name, &info) => LFS3_ERR_NOENT;
}
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
uint8_t rbuf[CHUNK];
for (lfs3_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
// rdonly rejected
lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, name, LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDWR) => LFS3_ERR_NOENT;
}
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// but we should still be able to read our orphans
for (lfs3_size_t o = 0; o < N; o++) {
lfs3_file_rewind(&lfs3, &orphans[o]) => 0;
prng = 42+o;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &orphans[o], rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 52;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close M orphans
for (lfs3_size_t o = 0; o < M && o < N; o++) {
lfs3_file_close(&lfs3, &orphans[o]) => 0;
}
// create a new unrelated file, this should trigger
// and orphan cleanup
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "catman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
prng = 62;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
}
lfs3_file_close(&lfs3, &file__);
// and now close our original unrelated file
lfs3_file_close(&lfs3, &file) => 0;
// and close the remaining orphans
for (lfs3_size_t o = M; o < N; o++) {
lfs3_file_close(&lfs3, &orphans[o]) => 0;
}
// now our file should exist, but none of our orphans
// via stat
for (lfs3_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
lfs3_stat(&lfs3, name, &info) => LFS3_ERR_NOENT;
}
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
for (lfs3_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
// rdonly rejected
lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, name, LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDWR) => LFS3_ERR_NOENT;
}
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 52;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0;
prng = 62;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_open]
defines.ORPHANS = [1, 2, 3, 100]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfs3_file_t orphans[ORPHANS-1];
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfs3_file_open(&lfs3, &orphans[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
lfs3_file_close(&lfs3, &orphans[i]) => 0;
}
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new file over the orphan
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_excl]
defines.ORPHANS = [1, 2, 3, 100]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfs3_file_t orphans[ORPHANS-1];
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfs3_file_open(&lfs3, &orphans[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
lfs3_file_close(&lfs3, &orphans[i]) => 0;
}
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new file over the zombie
//
// orphaned files will never exist again, so LFS3_O_EXCL should not
// fail here
//
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_mkdir]
defines.ORPHANS = [1, 2, 3, 100]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfs3_file_t orphans[ORPHANS-1];
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfs3_file_open(&lfs3, &orphans[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
lfs3_file_close(&lfs3, &orphans[i]) => 0;
}
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new dir over the orphan
lfs3_mkdir(&lfs3, "batman") => 0;
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_rm]
defines.ORPHANS = [1, 2, 3, 100]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfs3_file_t orphans[ORPHANS-1];
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfs3_file_open(&lfs3, &orphans[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
lfs3_file_close(&lfs3, &orphans[i]) => 0;
}
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// orphans aren't real, so remove should fail
lfs3_remove(&lfs3, "batman") => LFS3_ERR_NOENT;
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// just make sure things look ok
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_mv_dst]
defines.DIR = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.ORPHANS = [1, 2, 3, 100]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// make our src file before orphans, otherwise open just cleans
// things up
if (DIR) {
lfs3_mkdir(&lfs3, (INTERDIR) ? "a/datman" : "datman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/datman" : "datman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
}
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfs3_file_t orphans[ORPHANS-1];
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "c/aatman%03x" : "aatman%03x", i);
lfs3_file_open(&lfs3, &orphans[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
lfs3_file_close(&lfs3, &orphans[i]) => 0;
}
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename onto orphan
lfs3_rename(&lfs3,
(INTERDIR) ? "a/datman" : "datman",
(INTERDIR) ? "c/batman" : "batman") => 0;
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "c/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "c" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
if (DIR) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "c/batman" : "batman",
LFS3_O_RDONLY) => LFS3_ERR_ISDIR;
} else {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "c/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_orphan_mv_src]
defines.ORPHANS = [1, 2, 3, 100]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfs3_file_t orphans[ORPHANS-1];
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfs3_file_open(&lfs3, &orphans[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
lfs3_file_write(&lfs3, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs3_size_t i = 0; i < ORPHANS-1; i++) {
lfs3_file_close(&lfs3, &orphans[i]) => 0;
}
lfs3_file_close(&lfs3, &file) => 0;
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// orphans aren't real, so rename should fail
lfs3_rename(&lfs3, "batman", "catman") => LFS3_ERR_NOENT;
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// just make sure things look ok
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
lfs3_stat(&lfs3, "catman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that removed files never show up
[cases.test_stickynotes_zombie]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// syncing removed files is a noop
lfs3_file_sync(&lfs3, &file) => 0;
// resyncing removed files is an error
lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT;
// but we should still be able to read our file handle
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfs3_file_close(&lfs3, &file) => 0;
// because the file was removed, it should still not exist
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// even after a remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_posthumous]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// syncing removed files is a noop
lfs3_file_sync(&lfs3, &file) => 0;
// resyncing removed files is an error
lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT;
// but we should still be able to read our file handle
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfs3_file_close(&lfs3, &file) => 0;
// because the file was removed, it should still not exist
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// even after a remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_rwrw]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// create a second file
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// syncing removed files is a noop
lfs3_file_sync(&lfs3, &file) => 0;
// resyncing removed files is an error
lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT;
// syncing our second file should actually sync
lfs3_file_sync(&lfs3, &file__) => 0;
// second file should appear on disk now
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read both file handles
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfs3_file_close(&lfs3, &file) => 0;
lfs3_file_close(&lfs3, &file__) => 0;
// check disk again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_rwrw_posthumous]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// create a second file
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the second file
uint32_t prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
}
// write to the first file
prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// syncing removed files is a noop
lfs3_file_sync(&lfs3, &file) => 0;
// resyncing removed files is an error
lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT;
// syncing our second file should actually sync
lfs3_file_sync(&lfs3, &file__) => 0;
// second file should appear on disk now
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read both file handles
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfs3_file_close(&lfs3, &file) => 0;
lfs3_file_close(&lfs3, &file__) => 0;
// check disk again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_zombie_rwrwrw]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// create a second file
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file__) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// still doesn't exist
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// create a third file
lfs3_file_t file___;
lfs3_file_open(&lfs3, &file___, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the third file
prng = 42+2;
for (lfs3_off_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, &file___, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// syncing removed files is a noop
lfs3_file_sync(&lfs3, &file) => 0;
lfs3_file_sync(&lfs3, &file__) => 0;
// resyncing removed files is an error
lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT;
lfs3_file_resync(&lfs3, &file__) => LFS3_ERR_NOENT;
// syncing our third file should actually sync
lfs3_file_sync(&lfs3, &file___) => 0;
// third file should appear on disk now
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read all file handles
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfs3_file_close(&lfs3, &file) => 0;
lfs3_file_close(&lfs3, &file__) => 0;
lfs3_file_close(&lfs3, &file___) => 0;
// check disk again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_zombie_rwrwrw_posthumous]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// create a second file
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// need to sync so we can remove
lfs3_file_sync(&lfs3, &file__) => 0;
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
// still doesn't exist
// via stat
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// rdonly rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT;
// non-create rejected
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT;
// create a third file
lfs3_file_t file___;
lfs3_file_open(&lfs3, &file___, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the third file
uint32_t prng = 42+2;
for (lfs3_off_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, &file___, wbuf, CHUNK) => CHUNK;
}
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
}
// write to the first file
prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// still appears as a stickynote
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// syncing removed files is a noop
lfs3_file_sync(&lfs3, &file) => 0;
lfs3_file_sync(&lfs3, &file__) => 0;
// resyncing removed files is an error
lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT;
lfs3_file_resync(&lfs3, &file__) => LFS3_ERR_NOENT;
// syncing our third file should actually sync
lfs3_file_sync(&lfs3, &file___) => 0;
// third file should appear on disk now
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read all file handles
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file___) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfs3_file_close(&lfs3, &file) => 0;
lfs3_file_close(&lfs3, &file__) => 0;
lfs3_file_close(&lfs3, &file___) => 0;
// check disk again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
prng = 42+2;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_open]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, "batman") => 0;
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new file over the zombie
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_excl]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, "batman") => 0;
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new file over the zombie
//
// zombie files will never exist again, so LFS3_O_EXCL should not
// fail here
//
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_mkdir]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, "batman") => 0;
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// create a new dir over the zombie
lfs3_mkdir(&lfs3, "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_rm]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, "batman") => 0;
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// zombies aren't real, so remove should fail
lfs3_remove(&lfs3, "batman") => LFS3_ERR_NOENT;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// just make sure things look ok
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_mv_dst]
defines.DIR = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// make our src file before zombies, just in case
if (DIR) {
lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename onto zombie
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
if (DIR) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => LFS3_ERR_ISDIR;
} else {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
}
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombie_mv_src]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, "batman") => 0;
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// zombies aren't real, so rename should fail
lfs3_rename(&lfs3, "batman", "catman") => LFS3_ERR_NOENT;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// just make sure things look ok
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
lfs3_stat(&lfs3, "catman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test some other operations that can make zombies
[cases.test_stickynotes_zombify_mkdir]
defines.POSTHUMOUS = [false, true]
defines.CLOSE = [false, true]
defines.REMOUNT = [false, true]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a desync file, not a zombie yet
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// use mkdir on the same name, the file hasn't been created yet,
// so this shouldn't fail, but because we have an open file handle
// we create a zombie
lfs3_mkdir(&lfs3, "batman") => 0;
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
if (!CLOSE) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_zombify_mv_dst]
defines.ORPHAN = [false, true]
defines.DIR = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.POSTHUMOUS = [false, true]
defines.CLOSE = [false, true]
defines.REMOUNT = [false, true]
defines.MKCONSISTENT = [false, true]
if = [
'REMOUNT <= CLOSE',
'!DIR || ORPHAN',
]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// make our src file before zombies, just in case
if (DIR) {
lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a desync file, not a zombie yet
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
if (!ORPHAN) {
lfs3_file_sync(&lfs3, &zombie) => 0;
}
// rename onto the same name, this shouldn't fail (note the test
// conditions), but because we have an open file handle we create
// a zombie
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
if (DIR) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => LFS3_ERR_ISDIR;
} else {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
}
if (!CLOSE) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_fileonzombie_rm]
defines.DIR = [false, true]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, "batman") => 0;
// create a file on top of the zombie
if (DIR) {
lfs3_mkdir(&lfs3, "batman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// remove the file
lfs3_remove(&lfs3, "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// just make sure things look ok
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_fileonzombie_mv_dst]
defines.DIR = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// make our src file before zombies, just in case
if (DIR) {
lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0;
// create a file on top of the zombie
if (DIR) {
lfs3_mkdir(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename onto the file
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
if (DIR) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => LFS3_ERR_ISDIR;
} else {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
}
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we actually cleanup orphans correctly
[cases.test_stickynotes_cleanup]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# <=2 => grm-able
# >2 => requires orphans
defines.N = [0, 1, 2, 3, 10, 100]
defines.REMOUNT = [false, true]
defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3]
if = '(SIZE*N)/BLOCK_SIZE <= 32'
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
// create some unrelated files to make sure cleaning up orphans
// doesn't break other filesystem things
uint32_t bookend_prngs[2] = {0, 0};
if (BOOKENDS & 0x1) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "aatman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
if (BOOKENDS & 0x2) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "catman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
// create this many orphaned files
//
// anytime we close a not-yet-created desync file, we create an
// orphan, but note we need these to be different files, and we need
// to close them after all open calls, otherwise we just end up with
// one orphan (littlefs is eager to clean up orphans)
//
lfs3_file_t files[N];
for (lfs3_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfs3_file_open(&lfs3, &files[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
// remount? this has no effect on orphans
if (REMOUNT) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// calling lfs3_fs_mkconsistent should clean things up
lfs3_fs_mkconsistent(&lfs3) => 0;
// we should have cleaned up all grms/orphans
assert(lfs3.grm.queue[0] == 0);
assert(lfs3.grm.queue[1] == 0);
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfs3_mtree_weight(&lfs3)
<= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits));
lfs3_mdir_t mdir;
lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0;
assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS));
// check that other files are unaffected
for (int remount = 0; remount < 2; remount++) {
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (BOOKENDS & 0x1) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "aatman", LFS3_O_RDONLY) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file);
}
if (BOOKENDS & 0x2) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "catman", LFS3_O_RDONLY) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file);
}
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_cleanup_open]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# <=2 => grm-able
# >2 => requires orphans
defines.N = [0, 1, 2, 3, 10, 100]
defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3]
if = '(SIZE*N)/BLOCK_SIZE <= 32'
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
// create some unrelated opened files to make sure cleaning up
// orphans doesn't break other filesystem things
//
// note we leave these open in this test
lfs3_file_t bookend_files[2];
uint32_t bookend_prngs[2] = {0, 0};
if (BOOKENDS & 0x1) {
lfs3_file_open(&lfs3, &bookend_files[0], "aatman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &bookend_files[0], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &bookend_files[0]) => 0;
}
if (BOOKENDS & 0x2) {
lfs3_file_open(&lfs3, &bookend_files[1], "catman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &bookend_files[1], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &bookend_files[1]) => 0;
}
// create this many orphaned files
//
// anytime we close a not-yet-created desync file, we create an
// orphan, but note we need these to be different files, and we need
// to close them after all open calls, otherwise we just end up with
// one orphan (littlefs is eager to clean up orphans)
//
lfs3_file_t files[N];
for (lfs3_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfs3_file_open(&lfs3, &files[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
// calling lfs3_fs_mkconsistent should clean things up
lfs3_fs_mkconsistent(&lfs3) => 0;
// we should have cleaned up all grms/orphans
assert(lfs3.grm.queue[0] == 0);
assert(lfs3.grm.queue[1] == 0);
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfs3_mtree_weight(&lfs3)
<= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits));
lfs3_mdir_t mdir;
lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0;
assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS));
// check that other files are unaffected
for (int remount = 0; remount < 2; remount++) {
if (remount) {
if (BOOKENDS & 0x1) {
lfs3_file_close(&lfs3, &bookend_files[0]) => 0;
}
if (BOOKENDS & 0x2) {
lfs3_file_close(&lfs3, &bookend_files[1]) => 0;
}
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
if (BOOKENDS & 0x1) {
lfs3_file_open(&lfs3, &bookend_files[0], "aatman",
LFS3_O_RDONLY) => 0;
}
if (BOOKENDS & 0x2) {
lfs3_file_open(&lfs3, &bookend_files[1], "catman",
LFS3_O_RDONLY) => 0;
}
}
if (BOOKENDS & 0x1) {
lfs3_file_rewind(&lfs3, &bookend_files[0]) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &bookend_files[0], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x2) {
lfs3_file_rewind(&lfs3, &bookend_files[1]) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &bookend_files[1], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
if (BOOKENDS & 0x1) {
lfs3_file_close(&lfs3, &bookend_files[0]);
}
if (BOOKENDS & 0x2) {
lfs3_file_close(&lfs3, &bookend_files[1]);
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_cleanup_uncreat]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# <=2 => grm-able
# >2 => requires orphans
defines.N = [0, 1, 2, 3, 10, 100]
defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3]
if = '(SIZE*N)/BLOCK_SIZE <= 32'
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
// create some unrelated uncreat files to make sure cleaning up
// orphans doesn't break other filesystem things
//
// note we leave these uncreated + open in this test
lfs3_file_t bookend_files[2];
uint32_t bookend_prngs[2] = {0, 0};
if (BOOKENDS & 0x1) {
lfs3_file_open(&lfs3, &bookend_files[0], "aatman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &bookend_files[0], wbuf, SIZE) => SIZE;
}
if (BOOKENDS & 0x2) {
lfs3_file_open(&lfs3, &bookend_files[1], "catman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &bookend_files[1], wbuf, SIZE) => SIZE;
}
// create this many orphaned files
//
// anytime we close a not-yet-created desync file, we create an
// orphan, but note we need these to be different files, and we need
// to close them after all open calls, otherwise we just end up with
// one orphan (littlefs is eager to clean up orphans)
//
lfs3_file_t files[N];
for (lfs3_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfs3_file_open(&lfs3, &files[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
// calling lfs3_fs_mkconsistent should clean things up
lfs3_fs_mkconsistent(&lfs3) => 0;
// we should have cleaned up all grms/orphans
assert(lfs3.grm.queue[0] == 0);
assert(lfs3.grm.queue[1] == 0);
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfs3_mtree_weight(&lfs3)
<= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits));
lfs3_mdir_t mdir;
lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0;
assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS));
// check that other files are unaffected
for (int remount = 0; remount < 2; remount++) {
if (remount) {
if (BOOKENDS & 0x1) {
lfs3_file_close(&lfs3, &bookend_files[0]) => 0;
}
if (BOOKENDS & 0x2) {
lfs3_file_close(&lfs3, &bookend_files[1]) => 0;
}
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
if (BOOKENDS & 0x1) {
lfs3_file_open(&lfs3, &bookend_files[0], "aatman",
LFS3_O_RDONLY) => 0;
}
if (BOOKENDS & 0x2) {
lfs3_file_open(&lfs3, &bookend_files[1], "catman",
LFS3_O_RDONLY) => 0;
}
}
if (BOOKENDS & 0x1) {
lfs3_file_rewind(&lfs3, &bookend_files[0]) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &bookend_files[0], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x2) {
lfs3_file_rewind(&lfs3, &bookend_files[1]) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &bookend_files[1], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
if (BOOKENDS & 0x1) {
lfs3_file_close(&lfs3, &bookend_files[0]);
}
if (BOOKENDS & 0x2) {
lfs3_file_close(&lfs3, &bookend_files[1]);
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_cleanup_zombie]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# <=2 => grm-able
# >2 => requires orphans
defines.N = [0, 1, 2, 3, 10, 100]
defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3]
if = '(SIZE*N)/BLOCK_SIZE <= 32'
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
// create some unrelated zombie files to make sure cleaning up
// orphans doesn't break other filesystem things
//
// note we leave these zombied + open in this test
lfs3_file_t bookend_files[2];
uint32_t bookend_prngs[2] = {0, 0};
if (BOOKENDS & 0x1) {
lfs3_file_open(&lfs3, &bookend_files[0], "aatman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &bookend_files[0], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &bookend_files[0]) => 0;
lfs3_remove(&lfs3, "aatman") => 0;
}
if (BOOKENDS & 0x2) {
lfs3_file_open(&lfs3, &bookend_files[1], "catman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_write(&lfs3, &bookend_files[1], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &bookend_files[1]) => 0;
lfs3_remove(&lfs3, "catman") => 0;
}
// create this many orphaned files
//
// anytime we close a not-yet-created desync file, we create an
// orphan, but note we need these to be different files, and we need
// to close them after all open calls, otherwise we just end up with
// one orphan (littlefs is eager to clean up orphans)
//
lfs3_file_t files[N];
for (lfs3_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfs3_file_open(&lfs3, &files[i], name,
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
// calling lfs3_fs_mkconsistent should clean things up
lfs3_fs_mkconsistent(&lfs3) => 0;
// we should have cleaned up all grms/orphans
assert(lfs3.grm.queue[0] == 0);
assert(lfs3.grm.queue[1] == 0);
assert(!(lfs3.flags & LFS3_I_MKCONSISTENT));
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfs3_mtree_weight(&lfs3)
<= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits));
lfs3_mdir_t mdir;
lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0;
assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS));
// check that other files are unaffected
if (BOOKENDS & 0x1) {
lfs3_file_rewind(&lfs3, &bookend_files[0]) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &bookend_files[0], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x2) {
lfs3_file_rewind(&lfs3, &bookend_files[1]) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &bookend_files[1], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x1) {
lfs3_file_close(&lfs3, &bookend_files[0]);
}
if (BOOKENDS & 0x2) {
lfs3_file_close(&lfs3, &bookend_files[1]);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that file handles follow mvs correctly
#
# these doesn't really involve stickynotes, but it's the same internal
# circuitry, we might as well test them here
#
[cases.test_stickynotes_file_mv]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can rename
lfs3_file_sync(&lfs3, &file) => 0;
// rename the file
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// the file should have been renamed
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// but we should still be able to read our file handle
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfs3_file_close(&lfs3, &file) => 0;
// close should have no effect
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_file_mv_postmv]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// need to sync so we can rename
lfs3_file_sync(&lfs3, &file) => 0;
// rename the file
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// the file should have been renamed, but zero sized
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// but we should still be able to read our file handle
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
if (SYNC) {
// sync
lfs3_file_sync(&lfs3, &file) => 0;
// the data should now be visible
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// we should still be able to read our file handle
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close
lfs3_file_close(&lfs3, &file) => 0;
// the data should now be visible
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_file_mv_file_rwrw]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can rename
lfs3_file_sync(&lfs3, &file) => 0;
// create a second file
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_TRUNC) => 0;
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
}
// rename the file
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// the file should have been renamed
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// but we should still be able to read both file handles
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
if (SYNC) {
// sync, note the order
lfs3_file_sync(&lfs3, &file__) => 0;
lfs3_file_sync(&lfs3, &file) => 0;
// the second file's data should now be visible
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// both file handles should be updated
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close, note the order
lfs3_file_close(&lfs3, &file__) => 0;
lfs3_file_close(&lfs3, &file) => 0;
// close should have no effect
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_file_mv_rwrw_postmv]
defines.SIZE = [
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS3_MIN(64, SIZE)'
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
// need to sync so we can rename
lfs3_file_sync(&lfs3, &file) => 0;
// create a second file
lfs3_file_t file__;
lfs3_file_open(&lfs3, &file__, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_TRUNC) => 0;
// rename the file
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// write to the first file
uint32_t prng = 42;
for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK;
}
// write to the second file
prng = 42+1;
for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// the file should have been renamed, but zero sized
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0;
lfs3_file_close(&lfs3, &file_) => 0;
// but we should still be able to read both file handles
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
if (SYNC) {
// sync, note the order
lfs3_file_sync(&lfs3, &file__) => 0;
lfs3_file_sync(&lfs3, &file) => 0;
// the second file's data should now be visible
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// both file handles should be updated
lfs3_file_rewind(&lfs3, &file) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_rewind(&lfs3, &file__) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close, note the order
lfs3_file_close(&lfs3, &file__) => 0;
lfs3_file_close(&lfs3, &file) => 0;
// close should have no effect
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
// remount
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
prng = 42+1;
for (lfs3_off_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);
}
uint8_t rbuf[CHUNK];
lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfs3_file_close(&lfs3, &file_) => 0;
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_file_mv_mv_src]
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.POSTMV = [false, true]
defines.SYNC = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_sync(&lfs3, &file) => 0;
if (!POSTMV) {
lfs3_file_write(&lfs3, &file,
"catman!", strlen("catman!"))
=> strlen("catman!");
if (SYNC) {
lfs3_file_sync(&lfs3, &file) => 0;
}
}
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &file) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename while open
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTMV) {
lfs3_file_write(&lfs3, &file,
"catman!", strlen("catman!"))
=> strlen("catman!");
if (SYNC) {
lfs3_file_sync(&lfs3, &file) => 0;
}
}
// we should still be able to read our file
lfs3_file_rewind(&lfs3, &file) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
assert(info.size == strlen("catman!"));
} else {
assert(info.size == 0);
}
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS3_TYPE_REG);
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
assert(info.size == strlen("catman!"));
} else {
assert(info.size == 0);
}
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
} else {
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfs3_file_close(&lfs3, &file_) => 0;
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &file) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_file_fileonzombie_mv_src]
defines.DIR = [false, true]
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.POSTHUMOUS = [false, true]
# CLOSE=0 => don't close (before end of test)
# CLOSE=1 => close after op
# CLOSE=2 => close before op
defines.CLOSE = [0, 1, 2]
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after op
# REMOUNT=2 => remount before op
defines.REMOUNT = [0, 1, 2]
defines.MKCONSISTENT = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfs3_mkdir(&lfs3, "a") => 0;
lfs3_mkdir(&lfs3, "b") => 0;
lfs3_mkdir(&lfs3, "c") => 0;
}
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_close(&lfs3, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
if (DIR) {
lfs3_mkdir(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfs3_file_close(&lfs3, &file) => 0;
}
}
// create a zombie
lfs3_file_t zombie;
lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfs3_file_sync(&lfs3, &zombie) => 0;
lfs3_remove(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0;
// create a file on top of the zombie
if (DIR) {
lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfs3_file_close(&lfs3, &file) => 0;
}
if (CLOSE == 2) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// rename while open
lfs3_rename(&lfs3,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfs3_file_write(&lfs3, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our file
lfs3_file_rewind(&lfs3, &zombie) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
if (REMOUNT == 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfs3_fs_mkconsistent(&lfs3) => 0;
}
// make sure the new file is readable
// via stat
struct lfs3_info info;
lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS3_ERR_NOENT;
// via readdir
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs3_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == 0);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
// via open
if (DIR) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => LFS3_ERR_ISDIR;
} else {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS3_O_RDONLY) => 0;
uint8_t rbuf[256];
lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfs3_file_close(&lfs3, &file_) => 0;
}
if (CLOSE == 0) {
lfs3_file_close(&lfs3, &zombie) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# one particularly nasty case is renaming over an mdir split, since shrubs
# can be moved around quite a few times when that happens
#
# here we spam renames over an increasing number of files to hopefully hit
# that case
#
[cases.test_stickynotes_file_mv_split]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# keep unsynced data open?
defines.UNSYNC = [false, true]
# keep a desynced file open?
defines.DESYNC = [false, true]
# keep a zombie open?
defines.ZOMBIE = [false, true]
if = '(SIZE*N*(1+DESYNC+ZOMBIE))/BLOCK_SIZE <= 32'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// we need a file handle for each file + desync + zombie
lfs3_file_t files[N];
lfs3_file_t desyncs[N];
lfs3_file_t zombies[N];
// create this many files while renaming
uint32_t prng = 42;
uint32_t unsync_prng = 43;
uint32_t desync_prng = 44;
uint32_t zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// create a zombie?
if (ZOMBIE) {
lfs3_file_open(&lfs3, &zombies[i], "batman!!!",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
lfs3_file_write(&lfs3, &zombies[i], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &zombies[i]) => 0;
lfs3_remove(&lfs3, "batman!!!") => 0;
}
// always create as first file
lfs3_file_open(&lfs3, &files[i], "batman!!!",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &files[i]) => 0;
// keep unsynced data?
if (UNSYNC) {
lfs3_file_rewind(&lfs3, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
}
// keep a desynced file open?
if (DESYNC) {
lfs3_file_open(&lfs3, &desyncs[i], "batman!!!",
LFS3_O_RDWR | LFS3_O_DESYNC | LFS3_O_TRUNC) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
lfs3_file_write(&lfs3, &desyncs[i], wbuf, SIZE) => SIZE;
}
// rename!
char name[256];
sprintf(name, "batman%03x", i);
lfs3_rename(&lfs3, "batman!!!", name) => 0;
}
// check that renames worked
prng = 42;
struct lfs3_info info;
lfs3_stat(&lfs3, "batman!!!", &info) => LFS3_ERR_NOENT;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", i);
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// check size
assert(lfs3_file_size(&lfs3, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &files[i]) => 0;
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &desyncs[i]) => 0;
lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &zombies[i]) => 0;
lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try syncing any unsynced files
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_sync(&lfs3, &files[i]) => 0;
}
// check that sync worked
prng = (!UNSYNC) ? 42 : 43;
lfs3_stat(&lfs3, "batman!!!", &info) => LFS3_ERR_NOENT;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", i);
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// check size
assert(lfs3_file_size(&lfs3, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &files[i]) => 0;
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &desyncs[i]) => 0;
lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &zombies[i]) => 0;
lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try rewriting our open files
uint32_t rewrite_prng = 52;
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_rewind(&lfs3, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &files[i]) => 0;
}
// check that rewrites worked
rewrite_prng = 52;
lfs3_stat(&lfs3, "batman!!!", &info) => LFS3_ERR_NOENT;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", i);
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
// check that file handles are as expected
rewrite_prng = 52;
desync_prng = 44;
zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// check size
assert(lfs3_file_size(&lfs3, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &files[i]) => 0;
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &desyncs[i]) => 0;
lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &zombies[i]) => 0;
lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// cleanup files
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
if (DESYNC) {
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &desyncs[i]) => 0;
}
}
if (ZOMBIE) {
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &zombies[i]) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_stickynotes_file_mv_split_backwards]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# keep unsynced data open?
defines.UNSYNC = [false, true]
# keep a desynced file open?
defines.DESYNC = [false, true]
# keep a zombie open?
defines.ZOMBIE = [false, true]
if = '(SIZE*N*(1+DESYNC+ZOMBIE))/BLOCK_SIZE <= 32'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// we need a file handle for each file + desync + zombie
lfs3_file_t files[N];
lfs3_file_t desyncs[N];
lfs3_file_t zombies[N];
// create this many files while renaming
uint32_t prng = 42;
uint32_t unsync_prng = 43;
uint32_t desync_prng = 44;
uint32_t zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// create a zombie?
if (ZOMBIE) {
lfs3_file_open(&lfs3, &zombies[i], "batman???",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
lfs3_file_write(&lfs3, &zombies[i], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &zombies[i]) => 0;
lfs3_remove(&lfs3, "batman???") => 0;
}
// always create as last file
lfs3_file_open(&lfs3, &files[i], "batman???",
LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &files[i]) => 0;
// keep unsynced data?
if (UNSYNC) {
lfs3_file_rewind(&lfs3, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
}
// keep a desynced file open?
if (DESYNC) {
lfs3_file_open(&lfs3, &desyncs[i], "batman???",
LFS3_O_RDWR | LFS3_O_DESYNC | LFS3_O_TRUNC) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
lfs3_file_write(&lfs3, &desyncs[i], wbuf, SIZE) => SIZE;
}
// rename!
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfs3_rename(&lfs3, "batman???", name) => 0;
}
// check that renames worked
prng = 42;
struct lfs3_info info;
lfs3_stat(&lfs3, "batman???", &info) => LFS3_ERR_NOENT;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// check size
assert(lfs3_file_size(&lfs3, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &files[i]) => 0;
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &desyncs[i]) => 0;
lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &zombies[i]) => 0;
lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try syncing any unsynced files
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_sync(&lfs3, &files[i]) => 0;
}
// check that sync worked
prng = (!UNSYNC) ? 42 : 43;
lfs3_stat(&lfs3, "batman???", &info) => LFS3_ERR_NOENT;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// check size
assert(lfs3_file_size(&lfs3, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &files[i]) => 0;
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &desyncs[i]) => 0;
lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &zombies[i]) => 0;
lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try rewriting our open files
uint32_t rewrite_prng = 52;
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_rewind(&lfs3, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &files[i]) => 0;
}
// check that rewrites worked
rewrite_prng = 52;
lfs3_stat(&lfs3, "batman???", &info) => LFS3_ERR_NOENT;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
// check that file handles are as expected
rewrite_prng = 52;
desync_prng = 44;
zombie_prng = 45;
for (lfs3_size_t i = 0; i < N; i++) {
// check size
assert(lfs3_file_size(&lfs3, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &files[i]) => 0;
lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &desyncs[i]) => 0;
lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfs3_file_rewind(&lfs3, &zombies[i]) => 0;
lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// cleanup files
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
if (DESYNC) {
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &desyncs[i]) => 0;
}
}
if (ZOMBIE) {
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &zombies[i]) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# fuzz tests involving many uncreats + zombies, this gets a bit crazy
[cases.test_stickynotes_uz_fuzz]
# you probably need to flush if you expect errors
defines.FLUSH = false
defines.N = [1, 2, 4, 8, 16, 32, 64]
# do more ops than files to encourage file rewrites
defines.OPS = '2*N'
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.SEED = 'range(20)'
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// set up a simulation to compare against
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
bool *sim_isstickys = malloc(N*sizeof(bool));
lfs3_size_t sim_size = 0;
typedef struct sim_file {
lfs3_size_t x;
bool sticky;
bool zombie;
uint32_t prng;
lfs3_file_t file;
} sim_file_t;
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
lfs3_size_t sim_file_count = 0;
uint32_t prng = SEED;
for (lfs3_size_t i = 0; i < OPS; i++) {
nonsense:;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng) % 5;
// open a new file?
if (op == 0) {
if (sim_file_count >= N) {
goto nonsense;
}
// choose a pseudo-random number
lfs3_size_t x = TEST_PRNG(&prng) % N;
// already exists?
bool exist = false;
uint32_t wprng = 0;
bool sticky = true;
for (lfs3_size_t j = 0; j < sim_size; j++) {
if (sim[j] == x) {
exist = true;
wprng = sim_prngs[j];
sticky = sim_isstickys[j];
break;
}
}
// choose a random seed if we don't exist
if (!exist) {
wprng = TEST_PRNG(&prng);
sticky = true;
}
lfs3_size_t j = sim_file_count;
sim_files[j] = malloc(sizeof(sim_file_t));
// open the actual file
char name[256];
sprintf(name, "batman%03x", x);
lfs3_file_open(&lfs3, &sim_files[j]->file, name,
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH) ? LFS3_O_FLUSH : 0)) => 0;
// write some initial data if we don't exist
if (!exist || sticky) {
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs3_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE)
=> SIZE;
}
// open in our sim
sim_files[j]->x = x;
sim_files[j]->sticky = sticky;
sim_files[j]->zombie = false;
sim_files[j]->prng = wprng;
sim_file_count++;
// insert into our sim
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-k)*sizeof(bool));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
}
break;
}
}
// write/rewrite a file?
} else if (op == 1) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs3_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng);
// write to the file
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs3_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0;
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// update in our sim
for (lfs3_size_t k = 0;; k++) {
if (sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
// no longer sticky
sim_isstickys[k] = false;
break;
}
}
// update related sim files
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
// new prng
sim_files[k]->prng = wprng;
// no longer sticky
sim_files[k]->sticky = false;
}
}
}
// close a file?
} else if (op == 2) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs3_size_t x = sim_files[j]->x;
bool sticky = sim_files[j]->sticky;
bool zombie = sim_files[j]->zombie;
// this doesn't really test anything, but if we don't close
// files eventually everything will end up zombies
// close the file without affected disk
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
// clobber closed files to try to catch lingering references
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
// remove from list
free(sim_files[j]);
sim_files[j] = sim_files[sim_file_count-1];
sim_file_count -= 1;
// update our sim
if (sticky && !zombie) {
// orphaned?
bool orphan = true;
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
orphan = false;
}
}
// if we were never synced, delete from sim
if (orphan) {
for (lfs3_size_t k = 0;; k++) {
if (sim[k] == x) {
memmove(&sim[k], &sim[k+1],
(sim_size-(k+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[k], &sim_prngs[k+1],
(sim_size-(k+1))*sizeof(uint32_t));
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
(sim_size-(k+1))*sizeof(bool));
sim_size -= 1;
break;
}
}
}
}
// remove a file?
} else if (op == 3) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to delete
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
lfs3_size_t x = sim[j];
// delete this file
char name[256];
sprintf(name, "batman%03x", x);
lfs3_remove(&lfs3, name) => 0;
// delete from our sim
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
sim_size -= 1;
// mark any related sim files as zombied
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x) {
sim_files[k]->zombie = true;
}
}
// rename a file?
} else if (op == 4) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to rename, and a random number to
// rename to
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
lfs3_size_t x = sim[j];
lfs3_size_t y = TEST_PRNG(&prng) % N;
uint32_t wprng = sim_prngs[j];
bool sticky = sim_isstickys[j];
// rename this file
char old_name[256];
sprintf(old_name, "batman%03x", x);
char new_name[256];
sprintf(new_name, "batman%03x", y);
lfs3_rename(&lfs3, old_name, new_name) => 0;
// update our sim
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
sim_size -= 1;
if (k > j) {
k -= 1;
}
// update the prng/sticky
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
// just renaming
} else {
// first delete
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-1-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-1-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-1-k)*sizeof(bool));
sim[k] = y;
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
}
break;
}
}
// update any related sim files
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
// move source files
if (sim_files[k]->x == x) {
sim_files[k]->x = y;
// mark target files as zombied
} else if (sim_files[k]->x == y) {
sim_files[k]->zombie = true;
}
}
}
}
// check that disk matches our simulation
for (lfs3_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isstickys[j]) {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
}
}
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
struct lfs3_info info;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
for (lfs3_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isstickys[j]) {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
for (lfs3_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
uint32_t wprng = sim_prngs[j];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
uint8_t rbuf[SIZE];
if (sim_isstickys[j]) {
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0;
} else {
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
}
// check that our file handles match our simulation
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
uint32_t wprng = sim_files[j]->prng;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// clean up sim/lfs3
free(sim);
free(sim_prngs);
free(sim_isstickys);
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfs3_unmount(&lfs3) => 0;
'''
# fuzz tests involving many uncreats + zombies + dirs, this gets a bit crazy
[cases.test_stickynotes_uzd_fuzz]
# you probably need to flush if you expect errors
defines.FLUSH = false
defines.N = [1, 2, 4, 8, 16, 32, 64]
# do more ops than files to encourage file rewrites
defines.OPS = '2*N'
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.SEED = 'range(20)'
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// set up a simulation to compare against
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
bool *sim_isstickys = malloc(N*sizeof(bool));
bool *sim_isdirs = malloc(N*sizeof(bool));
lfs3_size_t sim_size = 0;
typedef struct sim_file {
lfs3_size_t x;
bool sticky;
bool zombie;
uint32_t prng;
lfs3_file_t file;
} sim_file_t;
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
lfs3_size_t sim_file_count = 0;
uint32_t prng = SEED;
for (lfs3_size_t i = 0; i < OPS; i++) {
nonsense:;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng) % 8;
// open a new file?
if (op == 0) {
if (sim_file_count >= N) {
goto nonsense;
}
// choose a pseudo-random number
lfs3_size_t x = TEST_PRNG(&prng) % N;
// already exists?
bool exist = true;
uint32_t wprng = 0;
bool sticky = true;
for (lfs3_size_t j = 0; j < sim_size; j++) {
if (sim[j] == x) {
if (sim_isdirs[j]) {
goto nonsense;
}
exist = true;
wprng = sim_prngs[j];
sticky = sim_isstickys[j];
break;
}
}
// choose a random seed if we don't exist
if (!exist) {
wprng = TEST_PRNG(&prng);
sticky = true;
}
lfs3_size_t j = sim_file_count;
sim_files[j] = malloc(sizeof(sim_file_t));
// open the actual file
char name[256];
sprintf(name, "batman%03x", x);
lfs3_file_open(&lfs3, &sim_files[j]->file, name,
LFS3_O_RDWR
| LFS3_O_CREAT
| ((FLUSH) ? LFS3_O_FLUSH : 0)) => 0;
// write some initial data if we don't exist
if (!exist || sticky) {
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs3_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE)
=> SIZE;
}
// open in our sim
sim_files[j]->x = x;
sim_files[j]->sticky = sticky;
sim_files[j]->zombie = false;
sim_files[j]->prng = wprng;
sim_file_count++;
// insert into our sim
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-k)*sizeof(bool));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-k)*sizeof(bool));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
sim_isdirs[k] = false;
}
break;
}
}
// write/rewrite a file?
} else if (op == 1) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs3_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng);
// write to the file
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs3_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE;
lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0;
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// update in our sim
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// new prng
sim_prngs[k] = wprng;
// no longer sticky
sim_isstickys[k] = false;
break;
}
}
// update related sim files
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
// new prng
sim_files[k]->prng = wprng;
// no longer sticky
sim_files[k]->sticky = false;
}
}
}
// close a file?
} else if (op == 2) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs3_size_t x = sim_files[j]->x;
lfs3_size_t sticky = sim_files[j]->sticky;
lfs3_size_t zombie = sim_files[j]->zombie;
// this doesn't really test anything, but if we don't close
// files eventually everything will end up zombies
// close the file without affected disk
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
// clobber closed files to try to catch lingering references
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
// remove from list
free(sim_files[j]);
sim_files[j] = sim_files[sim_file_count-1];
sim_file_count -= 1;
// update our sim
if (sticky && !zombie) {
// orphaned?
bool orphan = true;
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
orphan = false;
}
}
// if we were never synced, delete from sim
if (orphan) {
for (lfs3_size_t k = 0;; k++) {
if (sim[k] == x) {
memmove(&sim[k], &sim[k+1],
(sim_size-(k+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[k], &sim_prngs[k+1],
(sim_size-(k+1))*sizeof(uint32_t));
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
(sim_size-(k+1))*sizeof(bool));
memmove(&sim_isdirs[k], &sim_isdirs[k+1],
(sim_size-(k+1))*sizeof(bool));
sim_size -= 1;
break;
}
}
}
}
// remove a file?
} else if (op == 3) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to delete
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
lfs3_size_t x = sim[j];
// delete this file
char name[256];
sprintf(name, "batman%03x", x);
lfs3_remove(&lfs3, name) => 0;
// delete from our sim
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
(sim_size-(j+1))*sizeof(bool));
sim_size -= 1;
// mark any related sim files as zombied
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x) {
sim_files[k]->zombie = true;
}
}
// rename a file?
} else if (op == 4) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to rename, and a random number to
// rename to
lfs3_size_t j = TEST_PRNG(&prng) % sim_size;
lfs3_size_t x = sim[j];
lfs3_size_t y = TEST_PRNG(&prng) % N;
uint32_t wprng = sim_prngs[j];
bool sticky = sim_isstickys[j];
bool dir = sim_isdirs[j];
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// type mismatch?
if (sim_isdirs[k] != dir) {
goto nonsense;
}
}
break;
}
}
// rename this file
char old_name[256];
sprintf(old_name, "batman%03x", x);
char new_name[256];
sprintf(new_name, "batman%03x", y);
lfs3_rename(&lfs3, old_name, new_name) => 0;
// update our sim
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
(sim_size-(j+1))*sizeof(bool));
sim_size -= 1;
if (k > j) {
k -= 1;
}
// update the prng/sticky/dir
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
sim_isdirs[k] = dir;
// just renaming
} else {
// first delete
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs3_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
(sim_size-(j+1))*sizeof(bool));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-1-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-1-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-1-k)*sizeof(bool));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-1-k)*sizeof(bool));
sim[k] = y;
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
sim_isdirs[k] = dir;
}
break;
}
}
// update any related sim files
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
// move source files
if (sim_files[k]->x == x) {
sim_files[k]->x = y;
// mark target files as zombied
} else if (sim_files[k]->x == y) {
sim_files[k]->zombie = true;
}
}
// toss a directory into the mix
} else if (op == 5) {
// choose a pseudo-random number
lfs3_size_t x = TEST_PRNG(&prng) % N;
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
goto nonsense;
}
break;
}
}
// make the directory
char name[256];
sprintf(name, "batman%03x", x);
lfs3_mkdir(&lfs3, name) => 0;
// insert into our sim
for (lfs3_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs3_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-k)*sizeof(bool));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-k)*sizeof(bool));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = 0;
sim_isdirs[k] = true;
break;
}
}
// mark any related sim files as zombied
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x) {
sim_files[k]->zombie = true;
}
}
}
}
// check that disk matches our simulation
for (lfs3_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isdirs[j]) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else if (sim_isstickys[j]) {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
}
}
lfs3_dir_t dir;
lfs3_dir_open(&lfs3, &dir, "/") => 0;
struct lfs3_info info;
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
for (lfs3_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfs3_dir_read(&lfs3, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isdirs[j]) {
assert(info.type == LFS3_TYPE_DIR);
assert(info.size == 0);
} else if (sim_isstickys[j]) {
assert(info.type == LFS3_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
}
}
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
lfs3_dir_close(&lfs3, &dir) => 0;
for (lfs3_size_t j = 0; j < sim_size; j++) {
if (sim_isdirs[j]) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY)
=> LFS3_ERR_ISDIR;
} else {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
uint32_t wprng = sim_prngs[j];
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
uint8_t rbuf[SIZE];
if (sim_isstickys[j]) {
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0;
} else {
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
lfs3_file_close(&lfs3, &file) => 0;
}
}
// check that our file handles match our simulation
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
uint32_t wprng = sim_files[j]->prng;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// clean up sim/lfs3
free(sim);
free(sim_prngs);
free(sim_isstickys);
free(sim_isdirs);
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfs3_unmount(&lfs3) => 0;
'''