Files
littlefs/tests/test_stickynotes.toml
T
Christopher Haster 6c8fa28ae4 Reverted lfsr_mtree_*lookupleaf -> lfsr_mtree_lookup
Why?

- lfsr_mtree_lookupleaf vs lfsr_mtree_commit is inconsistent. Should
  lfsr_mdir_commit be called lfsr_mtree_commitleaf? That'd be weird.

  It's reasonable to call mdirs entries of the mtree, but it'd be weird
  to call rbyds entries of btrees, so the inconsistency there is
  expected.

- lfsr_mtree_lookup/lfsr_mtree_lookupnext (going mtree -> mdir) aren't
  actually useful.

- The lfsr_mtree_namelookup/lfsr_mtree_namelookupleaf split is just more
  of a headache than it's worth.

Saves a tiny bit of code:

           code          stack          ctx
  before: 35768           2392          640
  after:  35764 (-0.0%)   2392 (+0.0%)  640 (+0.0%)
2025-04-30 00:40:53 -05:00

11705 lines
382 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 = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// check that the file doesn't _really_ exist
lfs_t lfs_;
lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0;
// via stat
struct lfs_info info;
lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
lfsr_unmount(&lfs_) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// check that the file still doesn't _really_ exist
lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
lfsr_unmount(&lfs_) => 0;
}
if (SYNC) {
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// now it should show up
lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs_, &file_) => 0;
lfsr_unmount(&lfs_) => 0;
}
// close the file
lfsr_file_close(&lfs, &file) => 0;
// now it should show up
lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs_, &file_) => 0;
lfsr_unmount(&lfs_) => 0;
lfsr_unmount(&lfs) => 0;
// should still be there
lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs_, &file_) => 0;
lfsr_unmount(&lfs_) => 0;
'''
# test files don't exist until first sync/close, even under powerloss
[cases.test_stickynotes_uncreat_pl]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// create a file
//
// note the excl flag
lfsr_file_t file;
err = lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL);
assert(!err || err == LFS_ERR_EXIST);
if (err != LFS_ERR_EXIST) {
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// close the file
lfsr_file_close(&lfs, &file) => 0;
}
// we should be able to read the file now
lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0;
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
// and after remounting
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test files don't exist until first sync/close, even under powerloss
[cases.test_stickynotes_uncreat_many_pl]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
]
defines.CHUNK = 8
defines.N = 128
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// create N files
for (lfs_size_t j = 0; j < N; j++) {
// note the excl flag
char name[256];
sprintf(name, "batman%03x", j);
lfsr_file_t file;
err = lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL);
assert(!err || err == LFS_ERR_EXIST);
if (err != LFS_ERR_EXIST) {
// write to the file
uint32_t prng = 42 + j;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// close the file
lfsr_file_close(&lfs, &file) => 0;
}
}
// we should be able to read the files now
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
}
// and after remounting
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# test files only exist as stickynotes until first sync/close
[cases.test_stickynotes_uncreat_sync_wr]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
// but we should still recieve sync broadcasts on sync/close
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
if (SYNC) {
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the file
lfsr_file_close(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file__) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_uncreat_sync_rw]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// open a second reference
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// write to the second file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the second file
lfsr_file_close(&lfs, &file__) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_uncreat_wdwr]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
// open a second reference
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// and a third for checking sync broadcasts
lfsr_file_t file___;
lfsr_file_open(&lfs, &file___, "batman",
LFS_O_RDWR | LFS_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// write to the first file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// now it should show up
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the second file
lfsr_file_close(&lfs, &file__) => 0;
// now it should show up
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// now sync the first file, this should overwrite what is written
lfsr_file_sync(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &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
lfsr_file_close(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file___) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new file over the uncreat
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
// our uncreat should have been overwritten
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 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
//
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the excl file had no effect
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// attempt to mkdir
//
// this should fail because of the stickynote
//
lfsr_mkdir(&lfs, "batman") => LFS_ERR_EXIST;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the excl file had no effect
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// remove the stickynote
lfsr_remove(&lfs, "batman") => 0;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// the stickynote should be gone
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => LFS_ERR_NOENT;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename the stickynote
lfsr_rename(&lfs, "batman", "catman") => 0;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreate
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create another file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "catman",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename the stickynote
lfsr_rename(&lfs, "batman", "catman") => 0;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreate
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "catman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename the stickynote
lfsr_rename(&lfs, "batman", "catman") => 0;
// we should still be able to read our uncreate/file
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
lfsr_file_rewind(&lfs, &file) => 0;
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreate
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "catman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename onto the stickynote
lfsr_rename(&lfs, "catman", "batman") => 0;
// we should still be able to read our uncreate/file
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
lfsr_file_rewind(&lfs, &file) => 0;
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// test that the file replaced the stickynote
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreate
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// create another uncreate
lfsr_file_t uncreat_;
lfsr_file_open(&lfs, &uncreat_, "catman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat_, "WoO~", strlen("WoO~"))
=> strlen("WoO~");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename the stickynote
lfsr_rename(&lfs, "batman", "catman") => 0;
// we should still be able to read our uncreates
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
lfsr_file_rewind(&lfs, &uncreat_) => 0;
lfsr_file_read(&lfs, &uncreat_, rbuf, sizeof(rbuf))
=> strlen("WoO~");
assert(memcmp(rbuf, "WoO~", strlen("WoO~")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
lfsr_file_close(&lfs, &uncreat_) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
lfsr_file_close(&lfs, &uncreat_) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreate
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename the stickynote
lfsr_rename(&lfs, "batman", "batman") => 0;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the stickynote survived the rename
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// attempt to rename a dir onto the stickynote
//
// this should fail because of the stickynote
//
lfsr_mkdir(&lfs, "datman") => 0;
lfsr_rename(&lfs, "datman", "batman") => LFS_ERR_NOTDIR;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_stat(&lfs, "datman", &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// attempt to rename the stickynote onto a dir
//
// this should fail because of the stickynote
//
lfsr_mkdir(&lfs, "datman") => 0;
lfsr_rename(&lfs, "batman", "datman") => LFS_ERR_ISDIR;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_stat(&lfs, "datman", &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "datman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 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
//
lfsr_rename(&lfs, "batman", "/") => LFS_ERR_INVAL;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 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
//
lfsr_rename(&lfs, "batman", "no/batman") => LFS_ERR_NOENT;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the mkdir had no effect
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (CLOSE == 1) {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("WoOoOoOoOoO"));
} else {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
if (CLOSE == 1) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# test desync files don't exist until first sync + close
[cases.test_stickynotes_undesync]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// check that the file doesn't _really_ exist
lfs_t lfs_;
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
// via stat
struct lfs_info info;
lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
lfsr_unmount(&lfs_) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// check that the file still doesn't _really_ exist
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
lfsr_unmount(&lfs_) => 0;
}
if (SYNC) {
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// now it should show up
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs_, &file_) => 0;
lfsr_unmount(&lfs_) => 0;
}
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// close the file
lfsr_file_close(&lfs, &file) => 0;
// now it should show up
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs_, &file_) => 0;
lfsr_unmount(&lfs_) => 0;
lfsr_unmount(&lfs) => 0;
// should still be there
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs_, &dir, "/") => 0;
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs_, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs_, &dir) => 0;
// via open
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs_, &file_) => 0;
lfsr_unmount(&lfs_) => 0;
'''
# test desync files don't exist until first sync + close, even under powerloss
[cases.test_stickynotes_undesync_pl]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// create a desync file
//
// note the excl flag
lfsr_file_t file;
err = lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC);
assert(!err || err == LFS_ERR_EXIST);
if (err != LFS_ERR_EXIST) {
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// close the file
lfsr_file_close(&lfs, &file) => 0;
}
// we should be able to read the file now
lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0;
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
// and after remounting
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test desync files don't exist until first sync + close, even under powerloss
[cases.test_stickynotes_undesync_many_pl]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
]
defines.CHUNK = 8
defines.N = 128
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// create N desync files
lfsr_file_t files[N];
bool exists[N];
for (lfs_size_t j = 0; j < N; j++) {
// note the excl flag
char name[256];
sprintf(name, "batman%03x", j);
err = lfsr_file_open(&lfs, &files[j], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC);
assert(!err || err == LFS_ERR_EXIST);
exists[j] = (err == LFS_ERR_EXIST);
if (!exists[j]) {
// write to the file
uint32_t prng = 42 + j;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &files[j], wbuf, CHUNK) => CHUNK;
}
}
}
// sync and close
for (lfs_size_t j = 0; j < N; j++) {
if (!exists[j]) {
// sync the file
lfsr_file_sync(&lfs, &files[j]) => 0;
// close the file
lfsr_file_close(&lfs, &files[j]) => 0;
}
}
// we should be able to read the files now
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
}
// and after remounting
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "batman%03x", j);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t prng = 42 + j;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# test desync files aren't even openable until first sync + close
[cases.test_stickynotes_undesync_sync_wr]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
}
// but we should still recieve sync broadcasts on sync + close
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
if (SYNC) {
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// close the file
lfsr_file_close(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file__) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_undesync_sync_rw]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// open a second desync reference
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// uncommitted files appear as stickynotes
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// write to the second file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// close the second file
lfsr_file_close(&lfs, &file__) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_undesync_wdwr]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
// open a second desync reference
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
// and a third for checking sync broadcasts
lfsr_file_t file___;
lfsr_file_open(&lfs, &file___, "batman",
LFS_O_RDWR | LFS_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// write to the first file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
if (SYNC) {
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// now it should show up
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// close the second file
lfsr_file_close(&lfs, &file__) => 0;
// now it should show up
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// now sync the first file, this should overwrite what is written
lfsr_file_sync(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &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
lfsr_file_close(&lfs, &file) => 0;
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file___) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desynced uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new file over the uncreat
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desynced uncreat
lfsr_file_t uncreat;
lfsr_file_open(&lfs, &uncreat, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &uncreat,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new file over the uncreat
//
// while it's still possible for the desynced uncreat to create the
// file by explicitly calling lfsr_file_sync, for the most part we
// treat desynced files like zombies and allow excl creates
//
// this makes lfsr_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
//
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
// we should still be able to read our uncreat
lfsr_file_rewind(&lfs, &uncreat) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# test that desynced, and then closed, files don't show up
[cases.test_stickynotes_orphan]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
}
// close
lfsr_file_close(&lfs, &file) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// because the file was desynced, it should still not exist
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// even after a remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// because the file was desynced, it should still not exist
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_orphan_wdwr]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
# SYNC=0x1 => sync before orphaning
# SYNC=0x2 => sync after orphaning
defines.SYNC = [0, 1, 2, 3]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
// open a second reference
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// and a third for checking sync broadcasts
lfsr_file_t file___;
lfsr_file_open(&lfs, &file___, "batman",
LFS_O_RDWR | LFS_O_CREAT) => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// write to the first file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
}
if (SYNC & 0x1) {
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// now it should show up
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the first file, this should do nothing but discard the
// file contents
lfsr_file_close(&lfs, &file) => 0;
if (SYNC & 0x2) {
// sync the second file
lfsr_file_sync(&lfs, &file__) => 0;
// now it should show up
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close the second file
lfsr_file_close(&lfs, &file__) => 0;
// now it should show up
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// recieved sync broadcast?
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file___) => 0;
lfsr_unmount(&lfs) => 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 = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create N orphans
lfsr_file_t orphans[N];
for (lfs_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
lfsr_file_open(&lfs, &orphans[o], name,
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
uint32_t prng = 42+o;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &orphans[o], wbuf, CHUNK) => CHUNK;
}
}
// and an unrelated file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint32_t prng = 52;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// as far as the filesystem is concerned, none of the orphans exist
// via stat
struct lfs_info info;
for (lfs_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
}
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
uint8_t rbuf[CHUNK];
for (lfs_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
// rdonly rejected
lfsr_file_open(&lfs, &file_, name, LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, name, LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, name, LFS_O_RDWR) => LFS_ERR_NOENT;
}
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// but we should still be able to read our orphans
for (lfs_size_t o = 0; o < N; o++) {
lfsr_file_rewind(&lfs, &orphans[o]) => 0;
prng = 42+o;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &orphans[o], rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
lfsr_file_rewind(&lfs, &file) => 0;
prng = 52;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close M orphans
for (lfs_size_t o = 0; o < M && o < N; o++) {
lfsr_file_close(&lfs, &orphans[o]) => 0;
}
// create a new unrelated file, this should trigger
// and orphan cleanup
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "catman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
prng = 62;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
}
lfsr_file_close(&lfs, &file__);
// and now close our original unrelated file
lfsr_file_close(&lfs, &file) => 0;
// and close the remaining orphans
for (lfs_size_t o = M; o < N; o++) {
lfsr_file_close(&lfs, &orphans[o]) => 0;
}
// now our file should exist, but none of our orphans
// via stat
for (lfs_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
}
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, "catman", &info) => 0;
assert(strcmp(info.name, "catman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "catman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
for (lfs_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "aatman%03x", o);
// rdonly rejected
lfsr_file_open(&lfs, &file_, name, LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, name, LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, name, LFS_O_RDWR) => LFS_ERR_NOENT;
}
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 52;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0;
prng = 62;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfsr_file_t orphans[ORPHANS-1];
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfsr_file_open(&lfs, &orphans[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
lfsr_file_close(&lfs, &orphans[i]) => 0;
}
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new file over the orphan
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfsr_file_t orphans[ORPHANS-1];
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfsr_file_open(&lfs, &orphans[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
lfsr_file_close(&lfs, &orphans[i]) => 0;
}
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new file over the zombie
//
// orphaned files will never exist again, so LFS_O_EXCL should not
// fail here
//
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfsr_file_t orphans[ORPHANS-1];
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfsr_file_open(&lfs, &orphans[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
lfsr_file_close(&lfs, &orphans[i]) => 0;
}
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new dir over the orphan
lfsr_mkdir(&lfs, "batman") => 0;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfsr_file_t orphans[ORPHANS-1];
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfsr_file_open(&lfs, &orphans[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
lfsr_file_close(&lfs, &orphans[i]) => 0;
}
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// orphans aren't real, so remove should fail
lfsr_remove(&lfs, "batman") => LFS_ERR_NOENT;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// make our src file before orphans, otherwise open just cleans
// things up
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "a/datman" : "datman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/datman" : "datman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
}
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfsr_file_t orphans[ORPHANS-1];
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "c/aatman%03x" : "aatman%03x", i);
lfsr_file_open(&lfs, &orphans[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
lfsr_file_close(&lfs, &orphans[i]) => 0;
}
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename onto orphan
lfsr_rename(&lfs,
(INTERDIR) ? "a/datman" : "datman",
(INTERDIR) ? "c/batman" : "batman") => 0;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "c/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "c" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
if (DIR) {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "c/batman" : "batman",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "c/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create neighboring orphaned files
//
// more orphans requires different techniques for cleaning up orphans
lfsr_file_t orphans[ORPHANS-1];
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
char name[256];
sprintf(name, "aatman%03x", i);
lfsr_file_open(&lfs, &orphans[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &orphans[i],
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// create an orphaned file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
lfsr_file_write(&lfs, &file,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
// close all orphans at once, or else the open calls would just
// clean up each orphans
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
lfsr_file_close(&lfs, &orphans[i]) => 0;
}
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// orphans aren't real, so rename should fail
lfsr_rename(&lfs, "batman", "catman") => LFS_ERR_NOENT;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "catman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test that removed files never show up
[cases.test_stickynotes_zombie]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfsr_file_sync(&lfs, &file) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// syncing removed files is a noop
lfsr_file_sync(&lfs, &file) => 0;
// resyncing removed files is an error
lfsr_file_resync(&lfs, &file) => LFS_ERR_NOENT;
// but we should still be able to read our file handle
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfsr_file_close(&lfs, &file) => 0;
// because the file was removed, it should still not exist
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// even after a remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_zombie_posthumous]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// need to sync so we can remove
lfsr_file_sync(&lfs, &file) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// syncing removed files is a noop
lfsr_file_sync(&lfs, &file) => 0;
// resyncing removed files is an error
lfsr_file_resync(&lfs, &file) => LFS_ERR_NOENT;
// but we should still be able to read our file handle
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfsr_file_close(&lfs, &file) => 0;
// because the file was removed, it should still not exist
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// even after a remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_zombie_rwrw]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfsr_file_sync(&lfs, &file) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// syncing removed files is a noop
lfsr_file_sync(&lfs, &file) => 0;
// resyncing removed files is an error
lfsr_file_resync(&lfs, &file) => LFS_ERR_NOENT;
// syncing our second file should actually sync
lfsr_file_sync(&lfs, &file__) => 0;
// second file should appear on disk now
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read both file handles
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_close(&lfs, &file__) => 0;
// check disk again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_zombie_rwrw_posthumous]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// need to sync so we can remove
lfsr_file_sync(&lfs, &file) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the second file
uint32_t prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
}
// write to the first file
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// syncing removed files is a noop
lfsr_file_sync(&lfs, &file) => 0;
// resyncing removed files is an error
lfsr_file_resync(&lfs, &file) => LFS_ERR_NOENT;
// syncing our second file should actually sync
lfsr_file_sync(&lfs, &file__) => 0;
// second file should appear on disk now
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read both file handles
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_close(&lfs, &file__) => 0;
// check disk again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_zombie_zombie_rwrwrw]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfsr_file_sync(&lfs, &file) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can remove
lfsr_file_sync(&lfs, &file__) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// still doesn't exist
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// create a third file
lfsr_file_t file___;
lfsr_file_open(&lfs, &file___, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the third file
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file___, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// syncing removed files is a noop
lfsr_file_sync(&lfs, &file) => 0;
lfsr_file_sync(&lfs, &file__) => 0;
// resyncing removed files is an error
lfsr_file_resync(&lfs, &file) => LFS_ERR_NOENT;
lfsr_file_resync(&lfs, &file__) => LFS_ERR_NOENT;
// syncing our third file should actually sync
lfsr_file_sync(&lfs, &file___) => 0;
// third file should appear on disk now
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read all file handles
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_close(&lfs, &file__) => 0;
lfsr_file_close(&lfs, &file___) => 0;
// check disk again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_zombie_zombie_rwrwrw_posthumous]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// need to sync so we can remove
lfsr_file_sync(&lfs, &file) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// as far as the filesystem is concerned, this file does not exist anymore
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// need to sync so we can remove
lfsr_file_sync(&lfs, &file__) => 0;
// remove the file
lfsr_remove(&lfs, "batman") => 0;
// still doesn't exist
// via stat
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rdonly rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
// create a third file
lfsr_file_t file___;
lfsr_file_open(&lfs, &file___, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the third file
uint32_t prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file___, wbuf, CHUNK) => CHUNK;
}
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
}
// write to the first file
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// still appears as a stickynote
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
uint8_t rbuf[CHUNK];
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// syncing removed files is a noop
lfsr_file_sync(&lfs, &file) => 0;
lfsr_file_sync(&lfs, &file__) => 0;
// resyncing removed files is an error
lfsr_file_resync(&lfs, &file) => LFS_ERR_NOENT;
lfsr_file_resync(&lfs, &file__) => LFS_ERR_NOENT;
// syncing our third file should actually sync
lfsr_file_sync(&lfs, &file___) => 0;
// third file should appear on disk now
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// the perhaps surprising thing is we should still be able
// to read all file handles
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file___) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_close(&lfs, &file__) => 0;
lfsr_file_close(&lfs, &file___) => 0;
// check disk again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// via readdir
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
prng = 42+2;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, "batman") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new file over the zombie
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, "batman") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new file over the zombie
//
// zombie files will never exist again, so LFS_O_EXCL should not
// fail here
//
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, "batman") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// create a new dir over the zombie
lfsr_mkdir(&lfs, "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, "batman") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// zombies aren't real, so remove should fail
lfsr_remove(&lfs, "batman") => LFS_ERR_NOENT;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// make our src file before zombies, just in case
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
}
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename onto zombie
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
if (DIR) {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, "batman") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// zombies aren't real, so rename should fail
lfsr_rename(&lfs, "batman", "catman") => LFS_ERR_NOENT;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "catman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a desync file, not a zombie yet
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &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
lfsr_mkdir(&lfs, "batman") => 0;
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
if (!CLOSE) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// make our src file before zombies, just in case
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
}
// create a desync file, not a zombie yet
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
if (!ORPHAN) {
lfsr_file_sync(&lfs, &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
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
if (DIR) {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
if (!CLOSE) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, "batman") => 0;
// create a file on top of the zombie
if (DIR) {
lfsr_mkdir(&lfs, "batman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// remove the file
lfsr_remove(&lfs, "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// make our src file before zombies, just in case
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
}
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
// create a file on top of the zombie
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename onto the file
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our zombie
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
if (DIR) {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# test that we actually cleanup orphans correctly
[cases.test_stickynotes_cleanup]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = 'lfs.c'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_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) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "aatman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
lfsr_file_close(&lfs, &file) => 0;
}
if (BOOKENDS & 0x2) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "catman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
lfsr_file_close(&lfs, &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)
//
lfsr_file_t files[N];
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfsr_file_open(&lfs, &files[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &files[i]) => 0;
}
// remount? this has no effect on orphans
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// calling lfsr_fs_mkconsistent should clean things up
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfsr_mtree_weight(&lfs)
<= ((1+lfs_popc(BOOKENDS)) << lfs.mbits));
lfsr_mdir_t mdir;
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
assert(mdir.rbyd.weight <= 1+lfs_popc(BOOKENDS));
// check that other files are unaffected
for (int remount = 0; remount < 2; remount++) {
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (BOOKENDS & 0x1) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "aatman", LFS_O_RDONLY) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file);
}
if (BOOKENDS & 0x2) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "catman", LFS_O_RDONLY) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file);
}
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_cleanup_open]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = 'lfs.c'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_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
lfsr_file_t bookend_files[2];
uint32_t bookend_prngs[2] = {0, 0};
if (BOOKENDS & 0x1) {
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &bookend_files[0], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &bookend_files[0]) => 0;
}
if (BOOKENDS & 0x2) {
lfsr_file_open(&lfs, &bookend_files[1], "catman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &bookend_files[1], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &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)
//
lfsr_file_t files[N];
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfsr_file_open(&lfs, &files[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &files[i]) => 0;
}
// calling lfsr_fs_mkconsistent should clean things up
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfsr_mtree_weight(&lfs)
<= ((1+lfs_popc(BOOKENDS)) << lfs.mbits));
lfsr_mdir_t mdir;
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
assert(mdir.rbyd.weight <= 1+lfs_popc(BOOKENDS));
// check that other files are unaffected
for (int remount = 0; remount < 2; remount++) {
if (remount) {
if (BOOKENDS & 0x1) {
lfsr_file_close(&lfs, &bookend_files[0]) => 0;
}
if (BOOKENDS & 0x2) {
lfsr_file_close(&lfs, &bookend_files[1]) => 0;
}
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
if (BOOKENDS & 0x1) {
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
LFS_O_RDONLY) => 0;
}
if (BOOKENDS & 0x2) {
lfsr_file_open(&lfs, &bookend_files[1], "catman",
LFS_O_RDONLY) => 0;
}
}
if (BOOKENDS & 0x1) {
lfsr_file_rewind(&lfs, &bookend_files[0]) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &bookend_files[0], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x2) {
lfsr_file_rewind(&lfs, &bookend_files[1]) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &bookend_files[1], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
if (BOOKENDS & 0x1) {
lfsr_file_close(&lfs, &bookend_files[0]);
}
if (BOOKENDS & 0x2) {
lfsr_file_close(&lfs, &bookend_files[1]);
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_cleanup_uncreat]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = 'lfs.c'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_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
lfsr_file_t bookend_files[2];
uint32_t bookend_prngs[2] = {0, 0};
if (BOOKENDS & 0x1) {
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &bookend_files[0], wbuf, SIZE) => SIZE;
}
if (BOOKENDS & 0x2) {
lfsr_file_open(&lfs, &bookend_files[1], "catman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &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)
//
lfsr_file_t files[N];
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfsr_file_open(&lfs, &files[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &files[i]) => 0;
}
// calling lfsr_fs_mkconsistent should clean things up
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfsr_mtree_weight(&lfs)
<= ((1+lfs_popc(BOOKENDS)) << lfs.mbits));
lfsr_mdir_t mdir;
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
assert(mdir.rbyd.weight <= 1+lfs_popc(BOOKENDS));
// check that other files are unaffected
for (int remount = 0; remount < 2; remount++) {
if (remount) {
if (BOOKENDS & 0x1) {
lfsr_file_close(&lfs, &bookend_files[0]) => 0;
}
if (BOOKENDS & 0x2) {
lfsr_file_close(&lfs, &bookend_files[1]) => 0;
}
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
if (BOOKENDS & 0x1) {
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
LFS_O_RDONLY) => 0;
}
if (BOOKENDS & 0x2) {
lfsr_file_open(&lfs, &bookend_files[1], "catman",
LFS_O_RDONLY) => 0;
}
}
if (BOOKENDS & 0x1) {
lfsr_file_rewind(&lfs, &bookend_files[0]) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &bookend_files[0], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x2) {
lfsr_file_rewind(&lfs, &bookend_files[1]) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &bookend_files[1], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
if (BOOKENDS & 0x1) {
lfsr_file_close(&lfs, &bookend_files[0]);
}
if (BOOKENDS & 0x2) {
lfsr_file_close(&lfs, &bookend_files[1]);
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_cleanup_zombie]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = 'lfs.c'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_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
lfsr_file_t bookend_files[2];
uint32_t bookend_prngs[2] = {0, 0};
if (BOOKENDS & 0x1) {
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[0] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &bookend_files[0], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &bookend_files[0]) => 0;
lfsr_remove(&lfs, "aatman") => 0;
}
if (BOOKENDS & 0x2) {
lfsr_file_open(&lfs, &bookend_files[1], "catman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
bookend_prngs[1] = TEST_PRNG(&prng);
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_write(&lfs, &bookend_files[1], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &bookend_files[1]) => 0;
lfsr_remove(&lfs, "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)
//
lfsr_file_t files[N];
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "batman%03x", i);
lfsr_file_open(&lfs, &files[i], name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
}
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &files[i]) => 0;
}
// calling lfsr_fs_mkconsistent should clean things up
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS_I_MKCONSISTENT));
// double check the actual disk state, it's easy for littlefs to
// lie here
assert(lfsr_mtree_weight(&lfs)
<= ((1+lfs_popc(BOOKENDS)) << lfs.mbits));
lfsr_mdir_t mdir;
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
assert(mdir.rbyd.weight <= 1+lfs_popc(BOOKENDS));
// check that other files are unaffected
if (BOOKENDS & 0x1) {
lfsr_file_rewind(&lfs, &bookend_files[0]) => 0;
uint32_t prng_ = bookend_prngs[0];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &bookend_files[0], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x2) {
lfsr_file_rewind(&lfs, &bookend_files[1]) => 0;
uint32_t prng_ = bookend_prngs[1];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &bookend_files[1], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
if (BOOKENDS & 0x1) {
lfsr_file_close(&lfs, &bookend_files[0]);
}
if (BOOKENDS & 0x2) {
lfsr_file_close(&lfs, &bookend_files[1]);
}
lfsr_unmount(&lfs) => 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 = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.MKCONSISTENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can rename
lfsr_file_sync(&lfs, &file) => 0;
// rename the file
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// the file should have been renamed
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// but we should still be able to read our file handle
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
// close
lfsr_file_close(&lfs, &file) => 0;
// close should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_file_mv_postmv]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// need to sync so we can rename
lfsr_file_sync(&lfs, &file) => 0;
// rename the file
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// the file should have been renamed, but zero sized
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// but we should still be able to read our file handle
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
if (SYNC) {
// sync
lfsr_file_sync(&lfs, &file) => 0;
// the data should now be visible
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// we should still be able to read our file handle
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close
lfsr_file_close(&lfs, &file) => 0;
// the data should now be visible
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_file_mv_file_rwrw]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// write to the file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// need to sync so we can rename
lfsr_file_sync(&lfs, &file) => 0;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_TRUNC) => 0;
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
}
// rename the file
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// the file should have been renamed
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// but we should still be able to read both file handles
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
if (SYNC) {
// sync, note the order
lfsr_file_sync(&lfs, &file__) => 0;
lfsr_file_sync(&lfs, &file) => 0;
// the second file's data should now be visible
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// both file handles should be updated
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close, note the order
lfsr_file_close(&lfs, &file__) => 0;
lfsr_file_close(&lfs, &file) => 0;
// close should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_file_mv_rwrw_postmv]
defines.SIZE = [
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// need to sync so we can rename
lfsr_file_sync(&lfs, &file) => 0;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_TRUNC) => 0;
// rename the file
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
// write to the first file
uint32_t prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
}
// write to the second file
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
}
// mkconsistent should have no effect
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// the file should have been renamed, but zero sized
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
lfsr_file_close(&lfs, &file_) => 0;
// but we should still be able to read both file handles
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
if (SYNC) {
// sync, note the order
lfsr_file_sync(&lfs, &file__) => 0;
lfsr_file_sync(&lfs, &file) => 0;
// the second file's data should now be visible
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info)
=> LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// both file handles should be updated
lfsr_file_rewind(&lfs, &file) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_rewind(&lfs, &file__) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
}
// close, note the order
lfsr_file_close(&lfs, &file__) => 0;
lfsr_file_close(&lfs, &file) => 0;
// close should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
// remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
prng = 42+1;
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
uint8_t wbuf[CHUNK];
for (lfs_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[CHUNK];
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
}
lfsr_file_close(&lfs, &file_) => 0;
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_sync(&lfs, &file) => 0;
if (!POSTMV) {
lfsr_file_write(&lfs, &file,
"catman!", strlen("catman!"))
=> strlen("catman!");
if (SYNC) {
lfsr_file_sync(&lfs, &file) => 0;
}
}
if (CLOSE == 2) {
lfsr_file_close(&lfs, &file) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename while open
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTMV) {
lfsr_file_write(&lfs, &file,
"catman!", strlen("catman!"))
=> strlen("catman!");
if (SYNC) {
lfsr_file_sync(&lfs, &file) => 0;
}
}
// we should still be able to read our file
lfsr_file_rewind(&lfs, &file) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf))
=> strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &file) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
assert(info.size == strlen("catman!"));
} else {
assert(info.size == 0);
}
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
assert(info.type == LFS_TYPE_REG);
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
assert(info.size == strlen("catman!"));
} else {
assert(info.size == 0);
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
} else {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
}
lfsr_file_close(&lfs, &file_) => 0;
if (CLOSE == 0) {
lfsr_file_close(&lfs, &file) => 0;
}
lfsr_unmount(&lfs) => 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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create an interesting directory structure
if (INTERDIR) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
}
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_close(&lfs, &file) => 0;
}
// create our destination first, just in case
if (EXISTS) {
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
=> strlen("hmmmmmmmm");
lfsr_file_close(&lfs, &file) => 0;
}
}
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "c/datman" : "datman",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (!POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
lfsr_file_sync(&lfs, &zombie) => 0;
lfsr_remove(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
// create a file on top of the zombie
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
=> strlen("catman!");
lfsr_file_close(&lfs, &file) => 0;
}
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// rename while open
lfsr_rename(&lfs,
(INTERDIR) ? "c/datman" : "datman",
(INTERDIR) ? "a/batman" : "batman") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTHUMOUS) {
lfsr_file_write(&lfs, &zombie,
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
=> strlen("WoOoOoOoOoO");
}
// we should still be able to read our file
lfsr_file_rewind(&lfs, &zombie) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
=> strlen("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
}
if (MKCONSISTENT) {
lfsr_fs_mkconsistent(&lfs) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
// via readdir
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "batman") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("catman!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// via open
if (DIR) {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 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',
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// we need a file handle for each file + desync + zombie
lfsr_file_t files[N];
lfsr_file_t desyncs[N];
lfsr_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 (lfs_size_t i = 0; i < N; i++) {
// create a zombie?
if (ZOMBIE) {
lfsr_file_open(&lfs, &zombies[i], "batman!!!",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
lfsr_file_write(&lfs, &zombies[i], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &zombies[i]) => 0;
lfsr_remove(&lfs, "batman!!!") => 0;
}
// always create as first file
lfsr_file_open(&lfs, &files[i], "batman!!!",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &files[i]) => 0;
// keep unsynced data?
if (UNSYNC) {
lfsr_file_rewind(&lfs, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
}
// keep a desynced file open?
if (DESYNC) {
lfsr_file_open(&lfs, &desyncs[i], "batman!!!",
LFS_O_RDWR | LFS_O_DESYNC | LFS_O_TRUNC) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
lfsr_file_write(&lfs, &desyncs[i], wbuf, SIZE) => SIZE;
}
// rename!
char name[256];
sprintf(name, "batman%03x", i);
lfsr_rename(&lfs, "batman!!!", name) => 0;
}
// check that renames worked
prng = 42;
struct lfs_info info;
lfsr_stat(&lfs, "batman!!!", &info) => LFS_ERR_NOENT;
for (lfs_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", i);
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_t file;
uint8_t rbuf[SIZE];
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs_size_t i = 0; i < N; i++) {
// check size
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &files[i]) => 0;
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try syncing any unsynced files
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_sync(&lfs, &files[i]) => 0;
}
// check that sync worked
prng = (!UNSYNC) ? 42 : 43;
lfsr_stat(&lfs, "batman!!!", &info) => LFS_ERR_NOENT;
for (lfs_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", i);
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_t file;
uint8_t rbuf[SIZE];
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs_size_t i = 0; i < N; i++) {
// check size
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &files[i]) => 0;
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try rewriting our open files
uint32_t rewrite_prng = 52;
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_rewind(&lfs, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &files[i]) => 0;
}
// check that rewrites worked
rewrite_prng = 52;
lfsr_stat(&lfs, "batman!!!", &info) => LFS_ERR_NOENT;
for (lfs_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", i);
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfsr_file_t file;
uint8_t rbuf[SIZE];
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
// check that file handles are as expected
rewrite_prng = 52;
desync_prng = 44;
zombie_prng = 45;
for (lfs_size_t i = 0; i < N; i++) {
// check size
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &files[i]) => 0;
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// cleanup files
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &files[i]) => 0;
}
if (DESYNC) {
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &desyncs[i]) => 0;
}
}
if (ZOMBIE) {
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &zombies[i]) => 0;
}
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_stickynotes_file_mv_split_backwards]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.SIZE = [
'0',
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// we need a file handle for each file + desync + zombie
lfsr_file_t files[N];
lfsr_file_t desyncs[N];
lfsr_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 (lfs_size_t i = 0; i < N; i++) {
// create a zombie?
if (ZOMBIE) {
lfsr_file_open(&lfs, &zombies[i], "batman???",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
lfsr_file_write(&lfs, &zombies[i], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &zombies[i]) => 0;
lfsr_remove(&lfs, "batman???") => 0;
}
// always create as last file
lfsr_file_open(&lfs, &files[i], "batman???",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &files[i]) => 0;
// keep unsynced data?
if (UNSYNC) {
lfsr_file_rewind(&lfs, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
}
// keep a desynced file open?
if (DESYNC) {
lfsr_file_open(&lfs, &desyncs[i], "batman???",
LFS_O_RDWR | LFS_O_DESYNC | LFS_O_TRUNC) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
lfsr_file_write(&lfs, &desyncs[i], wbuf, SIZE) => SIZE;
}
// rename!
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfsr_rename(&lfs, "batman???", name) => 0;
}
// check that renames worked
prng = 42;
struct lfs_info info;
lfsr_stat(&lfs, "batman???", &info) => LFS_ERR_NOENT;
for (lfs_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_t file;
uint8_t rbuf[SIZE];
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs_size_t i = 0; i < N; i++) {
// check size
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &files[i]) => 0;
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try syncing any unsynced files
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_sync(&lfs, &files[i]) => 0;
}
// check that sync worked
prng = (!UNSYNC) ? 42 : 43;
lfsr_stat(&lfs, "batman???", &info) => LFS_ERR_NOENT;
for (lfs_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_t file;
uint8_t rbuf[SIZE];
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
// check that file handles are as expected
prng = (!UNSYNC) ? 42 : 43;
desync_prng = 44;
zombie_prng = 45;
for (lfs_size_t i = 0; i < N; i++) {
// check size
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &files[i]) => 0;
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// try rewriting our open files
uint32_t rewrite_prng = 52;
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_rewind(&lfs, &files[i]) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &files[i]) => 0;
}
// check that rewrites worked
rewrite_prng = 52;
lfsr_stat(&lfs, "batman???", &info) => LFS_ERR_NOENT;
for (lfs_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
lfsr_file_t file;
uint8_t rbuf[SIZE];
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
// check that file handles are as expected
rewrite_prng = 52;
desync_prng = 44;
zombie_prng = 45;
for (lfs_size_t i = 0; i < N; i++) {
// check size
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
// try reading the file
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &files[i]) => 0;
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// check any desynced files
if (DESYNC) {
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// check any zombied files
if (ZOMBIE) {
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
// cleanup files
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &files[i]) => 0;
}
if (DESYNC) {
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &desyncs[i]) => 0;
}
}
if (ZOMBIE) {
for (lfs_size_t i = 0; i < N; i++) {
lfsr_file_close(&lfs, &zombies[i]) => 0;
}
}
lfsr_unmount(&lfs) => 0;
'''
# fuzz tests involving many uncreats + zombies, this gets a bit crazy
[cases.test_stickynotes_uz_fuzz]
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',
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
bool *sim_isstickys = malloc(N*sizeof(bool));
lfs_size_t sim_size = 0;
typedef struct sim_file {
lfs_size_t x;
bool sticky;
bool zombie;
uint32_t prng;
lfsr_file_t file;
} sim_file_t;
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
lfs_size_t sim_file_count = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
nonsense:;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng) % 5;
// open a new file?
if (op == 0) {
if (sim_file_count >= N) {
goto nonsense;
}
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng) % N;
// already exists?
bool exist = false;
uint32_t wprng = 0;
bool sticky = true;
for (lfs_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;
}
lfs_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);
lfsr_file_open(&lfs, &sim_files[j]->file, name,
LFS_O_RDWR | LFS_O_CREAT) => 0;
// write some initial data if we don't exist
if (!exist || sticky) {
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE)
=> SIZE;
}
// 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 (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_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
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng);
// write to the file
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &sim_files[j]->file) => 0;
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// update in our sim
for (lfs_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 (lfs_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
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs_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
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
// clobber closed files to try to catch lingering references
memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_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 (lfs_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 (lfs_size_t k = 0;; k++) {
if (sim[k] == x) {
memmove(&sim[k], &sim[k+1],
(sim_size-(k+1))*sizeof(lfs_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
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
// delete this file
char name[256];
sprintf(name, "batman%03x", x);
lfsr_remove(&lfs, name) => 0;
// delete from our sim
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
sim_size -= 1;
// mark any related sim files as zombied
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x) {
sim_files[k]->zombie = true;
}
}
// rename a file?
} else if (op == 4) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to rename, and a random number to
// rename to
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
lfs_size_t y = TEST_PRNG(&prng) % N;
uint32_t wprng = sim_prngs[j];
bool 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);
lfsr_rename(&lfs, old_name, new_name) => 0;
// update our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
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(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-k)*sizeof(bool));
sim[k] = y;
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
}
break;
}
}
// update any related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
// move source files
if (sim_files[k]->x == x) {
sim_files[k]->x = y;
// mark target files as zombied
} else if (sim_files[k]->x == y) {
sim_files[k]->zombie = true;
}
}
}
}
// check that disk matches our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isstickys[j]) {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isstickys[j]) {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t wprng = sim_prngs[j];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
uint8_t rbuf[SIZE];
if (sim_isstickys[j]) {
lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0;
} else {
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
}
// check that our file handles match our simulation
for (lfs_size_t j = 0; j < sim_file_count; j++) {
uint32_t wprng = sim_files[j]->prng;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// clean up sim/lfs
free(sim);
free(sim_prngs);
free(sim_isstickys);
for (lfs_size_t j = 0; j < sim_file_count; j++) {
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfsr_unmount(&lfs) => 0;
'''
# fuzz tests involving many uncreats + zombies + dirs, this gets a bit crazy
[cases.test_stickynotes_uzd_fuzz]
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',
'FILE_CACHE_SIZE/2',
'2*FILE_CACHE_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 = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
bool *sim_isstickys = malloc(N*sizeof(bool));
bool *sim_isdirs = malloc(N*sizeof(bool));
lfs_size_t sim_size = 0;
typedef struct sim_file {
lfs_size_t x;
bool sticky;
bool zombie;
uint32_t prng;
lfsr_file_t file;
} sim_file_t;
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
lfs_size_t sim_file_count = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
nonsense:;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng) % 8;
// open a new file?
if (op == 0) {
if (sim_file_count >= N) {
goto nonsense;
}
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng) % N;
// already exists?
bool exist = true;
uint32_t wprng = 0;
bool sticky = true;
for (lfs_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;
}
lfs_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);
lfsr_file_open(&lfs, &sim_files[j]->file, name,
LFS_O_RDWR | LFS_O_CREAT) => 0;
// write some initial data if we don't exist
if (!exist || sticky) {
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE)
=> SIZE;
}
// 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 (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_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
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng);
// write to the file
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &sim_files[j]->file) => 0;
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// update in our sim
for (lfs_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 (lfs_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
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs_size_t x = sim_files[j]->x;
lfs_size_t sticky = sim_files[j]->sticky;
lfs_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
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
// clobber closed files to try to catch lingering references
memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_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 (lfs_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 (lfs_size_t k = 0;; k++) {
if (sim[k] == x) {
memmove(&sim[k], &sim[k+1],
(sim_size-(k+1))*sizeof(lfs_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
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
// delete this file
char name[256];
sprintf(name, "batman%03x", x);
lfsr_remove(&lfs, name) => 0;
// delete from our sim
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_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 (lfs_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
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
lfs_size_t y = TEST_PRNG(&prng) % N;
uint32_t wprng = sim_prngs[j];
bool sticky = sim_isstickys[j];
bool dir = sim_isdirs[j];
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// type mismatch?
if (sim_isdirs[k] != 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);
lfsr_rename(&lfs, old_name, new_name) => 0;
// update our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
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(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
(sim_size-(j+1))*sizeof(bool));
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
(sim_size-(j+1))*sizeof(bool));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
(sim_size-k)*sizeof(bool));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-k)*sizeof(bool));
sim[k] = y;
sim_prngs[k] = wprng;
sim_isstickys[k] = sticky;
sim_isdirs[k] = dir;
}
break;
}
}
// update any related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
// move source files
if (sim_files[k]->x == x) {
sim_files[k]->x = y;
// mark target files as zombied
} else if (sim_files[k]->x == y) {
sim_files[k]->zombie = true;
}
}
// toss a directory into the mix
} else if (op == 5) {
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng) % N;
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
goto nonsense;
}
break;
}
}
// make the directory
char name[256];
sprintf(name, "batman%03x", x);
lfsr_mkdir(&lfs, name) => 0;
// insert into our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_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 (lfs_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 (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isdirs[j]) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else if (sim_isstickys[j]) {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isdirs[j]) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else if (sim_isstickys[j]) {
assert(info.type == LFS_TYPE_STICKYNOTE);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
if (sim_isdirs[j]) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY)
=> LFS_ERR_ISDIR;
} else {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t wprng = sim_prngs[j];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
uint8_t rbuf[SIZE];
if (sim_isstickys[j]) {
lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0;
} else {
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
lfsr_file_close(&lfs, &file) => 0;
}
}
// check that our file handles match our simulation
for (lfs_size_t j = 0; j < sim_file_count; j++) {
uint32_t wprng = sim_files[j]->prng;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// clean up sim/lfs
free(sim);
free(sim_prngs);
free(sim_isstickys);
free(sim_isdirs);
for (lfs_size_t j = 0; j < sim_file_count; j++) {
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfsr_unmount(&lfs) => 0;
'''