Files
littlefs/tests/test_forphans.toml
T
Christopher Haster a63b8e1527 Dropped internal LFS_i_UNTIDY pseudo-alias flag
We really shouldn't have two names for the same thing, it just makes
things more confusing, even if the public name doesn't quite match the
internal usage. Especially now that we internally rely on these being
the same flag.

This renames LFS_i_UNTIDY -> LFS_I_MKCONSISTENT and drops the untidy/
mktidy naming internally.

No code changes.
2025-02-08 14:53:47 -06:00

8858 lines
290 KiB
TOML

# Test orphaned files and their various use cases
after = ['test_fwrite', 'test_fsync']
# Some specific tests
[cases.test_forphans_uncreat]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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_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;
}
// 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;
'''
[cases.test_forphans_uncreat_pl]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
'''
[cases.test_forphans_uncreat_many_pl]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
'''
[cases.test_forphans_uncreat_sync_wr]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
}
// 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);
}
}
// 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_forphans_uncreat_sync_rw]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
}
// 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 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;
}
// 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;
}
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_forphans_uncreat_desync_wdwr]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
}
// 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 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;
}
// 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;
}
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;
// 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_forphans_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_forphans_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
//
// counterintuitively, we _do_ error on an attempt to create an excl
// file when there is an uncreat, even though the file doesn't exist
// yet
//
// otherwise it's easy to create the same file twice with excl,
// which isn't very useful and 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
if (CLOSE == 1) {
// 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("WoOoOoOoOoO"));
// 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("WoOoOoOoOoO"));
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("WoOoOoOoOoO");
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
} else {
// 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;
}
if (CLOSE == 0) {
lfsr_file_close(&lfs, &uncreat) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_forphans_uncreat_desync_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_forphans_uncreat_desync_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;
'''
[cases.test_forphans_orphan]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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_forphans_orphan_wdwr]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
}
// 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 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;
}
// 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;
}
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_forphans_orphan_unrelated]
# different number of orphan require different methods of cleanup
defines.N = 'range(6)'
defines.M = 'range(6)'
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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) => 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_;
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;
}
// 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 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_forphans_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_forphans_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_forphans_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_forphans_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_forphans_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_forphans_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;
'''
[cases.test_forphans_zombie]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
// removed files can not be synced
lfsr_file_sync(&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_forphans_zombie_posthumous]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
// removed files can not be synced
lfsr_file_sync(&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_forphans_zombie_rwrw]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
// 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;
}
// 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;
// removed files can not be synced
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
// but we should be able to sync our second file just fine
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_forphans_zombie_rwrw_posthumous]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
// 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;
}
// 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;
// removed files can not be synced
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
// but we should be able to sync our second file just fine
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_forphans_zombie_zombie_rwrwrw]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
// 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;
// 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;
}
// 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;
// removed files can not be synced
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
lfsr_file_sync(&lfs, &file__) => LFS_ERR_NOENT;
// but we should be able to sync our third file just fine
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_forphans_zombie_zombie_rwrwrw_posthumous]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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;
// 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;
// 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;
}
// 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;
// removed files can not be synced
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
lfsr_file_sync(&lfs, &file__) => LFS_ERR_NOENT;
// but we should be able to sync our third file just fine
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_forphans_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_forphans_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_forphans_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_forphans_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_forphans_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_forphans_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;
'''
[cases.test_forphans_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 file, not a zombie yet
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");
}
// 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_forphans_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 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) => 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_forphans_file_on_zombie_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_forphans_file_on_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;
// 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_forphans_cleanup]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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.mdir_bits));
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_forphans_cleanup_open]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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.mdir_bits));
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_forphans_cleanup_uncreat]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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.mdir_bits));
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_forphans_cleanup_zombie]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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.mdir_bits));
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;
'''
# these doesn't really involve scratch files, but we might as well test
# them here
[cases.test_forphans_mv]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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_forphans_mv_postmv]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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_forphans_mv_rwrw]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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_forphans_mv_rwrw_postmv]
defines.SIZE = [
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_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_forphans_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_forphans_file_on_zombie_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_forphans_mv_split]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.SIZE = [
'0',
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# 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_forphans_mv_split_backwards]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.SIZE = [
'0',
'FILE_BUFFER_SIZE/2',
'2*FILE_BUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
# 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_forphans_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_BUFFER_SIZE/2',
'2*FILE_BUFFER_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));
lfs_size_t sim_size = 0;
typedef struct sim_file {
lfs_size_t x;
bool uncreat;
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 uncreat = true;
uint32_t wprng = 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
if (sim[j] == x) {
uncreat = false;
wprng = sim_prngs[j];
break;
}
}
// choose a random seed if we don't exist
if (uncreat) {
wprng = TEST_PRNG(&prng);
}
// open in our sim
lfs_size_t j = sim_file_count;
sim_files[j] = malloc(sizeof(sim_file_t));
sim_files[j]->x = x;
sim_files[j]->uncreat = uncreat;
sim_files[j]->zombie = false;
sim_files[j]->prng = wprng;
sim_file_count++;
// open the actual file
char name[256];
sprintf(name, "batman%03x", x);
lfsr_file_open(&lfs, &sim_files[j]->file, name,
LFS_O_RDWR | LFS_O_CREAT) => 0;
// write some initial data if we don't exist
if (uncreat) {
uint8_t wbuf[SIZE];
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
}
// write/rewrite a file?
} else if (op == 1) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng);
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// insert into our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = wprng;
}
break;
}
}
// update related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
sim_files[k]->uncreat = false;
sim_files[k]->prng = wprng;
}
}
}
// write to the file
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &sim_files[j]->file)
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
// close a file?
} else if (op == 2) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
// this doesn't really test anything, but if we don't close
// files eventually everything will end up zombies
// close the file without affected disk
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
// remove from list
free(sim_files[j]);
sim_files[j] = sim_files[sim_file_count-1];
sim_file_count -= 1;
// remove a file?
} else if (op == 3) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to delete
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
// delete from our sim
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
sim_size -= 1;
// mark any related sim files as zombied
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x) {
sim_files[k]->zombie = true;
}
}
// delete this file
char name[256];
sprintf(name, "batman%03x", x);
lfsr_remove(&lfs, name) => 0;
// rename a file?
} else if (op == 4) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to rename, and a random number to
// rename to
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
lfs_size_t y = TEST_PRNG(&prng) % N;
uint32_t wprng = sim_prngs[j];
// update our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
sim_size -= 1;
if (k > j) {
k -= 1;
}
// update the prng
sim_prngs[k] = wprng;
// just renaming
} else {
// first delete
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
sim[k] = y;
sim_prngs[k] = wprng;
}
break;
}
}
// update any related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
// move source files
if (sim_files[k]->x == x) {
sim_files[k]->x = y;
// mark target files as zombied
} else if (sim_files[k]->x == y) {
sim_files[k]->zombie = true;
}
}
// rename this file
char old_name[256];
sprintf(old_name, "batman%03x", x);
char new_name[256];
sprintf(new_name, "batman%03x", y);
lfsr_rename(&lfs, old_name, new_name) => 0;
}
}
// check that disk matches our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t wprng = sim_prngs[j];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
// check that our file handles match our simulation
for (lfs_size_t j = 0; j < sim_file_count; j++) {
uint32_t wprng = sim_files[j]->prng;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// clean up sim/lfs
free(sim);
free(sim_prngs);
for (lfs_size_t j = 0; j < sim_file_count; j++) {
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfsr_unmount(&lfs) => 0;
'''
# fuzz tests involving many uncreats + zombies + dirs, this gets a bit crazy
[cases.test_forphans_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_BUFFER_SIZE/2',
'2*FILE_BUFFER_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_isdirs = malloc(N*sizeof(bool));
lfs_size_t sim_size = 0;
typedef struct sim_file {
lfs_size_t x;
bool uncreat;
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 uncreat = true;
uint32_t wprng = 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
if (sim[j] == x) {
if (sim_isdirs[j]) {
goto nonsense;
}
uncreat = false;
wprng = sim_prngs[j];
break;
}
}
// choose a random seed if we don't exist
if (uncreat) {
wprng = TEST_PRNG(&prng);
}
// open in our sim
lfs_size_t j = sim_file_count;
sim_files[j] = malloc(sizeof(sim_file_t));
sim_files[j]->x = x;
sim_files[j]->uncreat = uncreat;
sim_files[j]->zombie = false;
sim_files[j]->prng = wprng;
sim_file_count++;
// open the actual file
char name[256];
sprintf(name, "batman%03x", x);
lfsr_file_open(&lfs, &sim_files[j]->file, name,
LFS_O_RDWR | LFS_O_CREAT) => 0;
// write some initial data if we don't exist
if (uncreat) {
uint8_t wbuf[SIZE];
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
}
// write/rewrite a file?
} else if (op == 1) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
lfs_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng);
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// insert into our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-k)*sizeof(bool));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = wprng;
sim_isdirs[k] = false;
}
break;
}
}
// update related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
sim_files[k]->uncreat = false;
sim_files[k]->prng = wprng;
}
}
}
// write to the file
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
lfsr_file_sync(&lfs, &sim_files[j]->file)
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
// close a file?
} else if (op == 2) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
// this doesn't really test anything, but if we don't close
// files eventually everything will end up zombies
// close the file without affected disk
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
// remove from list
free(sim_files[j]);
sim_files[j] = sim_files[sim_file_count-1];
sim_file_count -= 1;
// remove a file?
} else if (op == 3) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to delete
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
// delete from our sim
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
(sim_size-(j+1))*sizeof(bool));
sim_size -= 1;
// mark any related sim files as zombied
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x) {
sim_files[k]->zombie = true;
}
}
// delete this file
char name[256];
sprintf(name, "batman%03x", x);
lfsr_remove(&lfs, name) => 0;
// rename a file?
} else if (op == 4) {
if (sim_size == 0) {
goto nonsense;
}
// choose a random file to rename, and a random number to
// rename to
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
lfs_size_t x = sim[j];
lfs_size_t y = TEST_PRNG(&prng) % N;
uint32_t wprng = sim_prngs[j];
bool isdir = sim_isdirs[j];
// update our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// type mismatch?
if (sim_isdirs[k] != isdir) {
goto nonsense;
}
// delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
(sim_size-(j+1))*sizeof(bool));
sim_size -= 1;
if (k > j) {
k -= 1;
}
// update the prng
sim_prngs[k] = wprng;
// just renaming
} else {
// first delete
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
(sim_size-(j+1))*sizeof(bool));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-k)*sizeof(bool));
sim[k] = y;
sim_prngs[k] = wprng;
sim_isdirs[k] = isdir;
}
break;
}
}
// update any related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
// move source files
if (sim_files[k]->x == x) {
sim_files[k]->x = y;
// mark target files as zombied
} else if (sim_files[k]->x == y) {
sim_files[k]->zombie = true;
}
}
// rename this file
char old_name[256];
sprintf(old_name, "batman%03x", x);
char new_name[256];
sprintf(new_name, "batman%03x", y);
lfsr_rename(&lfs, old_name, new_name) => 0;
// toss a directory into the mix
} else if (op == 5) {
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng) % N;
// insert into our sim, use negative numbers for dirs
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
goto nonsense;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-k)*sizeof(bool));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = 0;
sim_isdirs[k] = true;
}
break;
}
}
// mark any related sim files as zombied
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x) {
sim_files[k]->zombie = true;
}
}
// make the directory
char name[256];
sprintf(name, "batman%03x", x);
lfsr_mkdir(&lfs, name) => 0;
}
}
// check that disk matches our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isdirs[j]) {
assert(info.type == LFS_TYPE_DIR);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
if (sim_isdirs[j]) {
assert(info.type == LFS_TYPE_DIR);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
if (sim_isdirs[j]) {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
char name[256];
sprintf(name, "batman%03x", sim[j]);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t wprng = sim_prngs[j];
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
}
}
// check that our file handles match our simulation
for (lfs_size_t j = 0; j < sim_file_count; j++) {
uint32_t wprng = sim_files[j]->prng;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
// clean up sim/lfs
free(sim);
free(sim_prngs);
for (lfs_size_t j = 0; j < sim_file_count; j++) {
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfsr_unmount(&lfs) => 0;
'''