Files
littlefs/tests/test_forphans.toml
T
Christopher Haster 4d76551d6b Fixed parse errors in prettyasserts.py caused by ternary operators
Because of course ternary operators would cause problems.

The two problem:

  LFS_ASSERT((exists) ? !err : err == LFS_ERR_NOENT);
  lfsr_file_sync(&lfs, &file) => (zombie) ? 0 : LFS_ERR_NOENT;

We could work around these with parentheses, but with different assert
parsers floating around this issue is likely to crop up again in the
future.

Fortunately this just required separate "sep" vs "term" rules and a bit
more strict parsing.
2024-05-22 18:50:54 -05:00

6701 lines
221 KiB
TOML

# Test orphaned files and their various use cases
after = ['test_fwrite', 'test_fsync']
# Some specific tests
[cases.test_forphans_create]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
// check that the file doesn't _really_ exist
lfs_t lfs_;
lfsr_mount(&lfs_, CFG) => 0;
// via stat
struct lfs_info info;
lfsr_stat(&lfs_, "gello", &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_, "gello", 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;
// check that the file still doesn't _really_ exist
lfsr_mount(&lfs_, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "gello", &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_, "gello", 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_, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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_, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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_, CFG) => 0;
// via stat
lfsr_stat(&lfs_, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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_create_pl]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_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, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create a file
//
// note the excl flag
lfsr_file_t file;
err = lfsr_file_open(&lfs, &file, "gello",
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, "gello", 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, CFG) => 0;
lfsr_file_open(&lfs, &file, "gello", 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_create_many_pl]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
]
defines.CHUNK = 8
defines.N = 128
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create N files
for (lfs_size_t j = 0; j < N; j++) {
// note the excl flag
char name[256];
sprintf(name, "gello%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, "gello%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, CFG) => 0;
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "gello%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_create_sync_wr]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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__, "gello",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
if (SYNC) {
// sync the file
lfsr_file_sync(&lfs, &file) => 0;
// now it should show up
// via stat
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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_unmount(&lfs) => 0;
'''
[cases.test_forphans_create_sync_rw]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
// open a second reference
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "gello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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_unmount(&lfs) => 0;
'''
[cases.test_forphans_create_desync_wdwr]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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__, "gello",
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___, "gello",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 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;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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_orphan]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 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;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
}
// close
lfsr_file_close(&lfs, &file) => 0;
// because the file was desynced, it should still not exist
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
// even after a remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
// because the file was desynced, it should still not exist
// via stat
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_forphans_orphan_wdwr]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a desync file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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__, "gello",
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___, "gello",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 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;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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 = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create N orphans
lfsr_file_t orphans[N];
for (lfs_size_t o = 0; o < N; o++) {
char name[256];
sprintf(name, "fello%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, "gello",
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;
}
// 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, "fello%03x", o);
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
}
lfsr_stat(&lfs, "gello", &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, "fello%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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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__, "hello",
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, "fello%03x", o);
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
}
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 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, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 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, "fello%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_, "gello", 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_, "hello", 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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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, "fello%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, "gello",
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, CFG) => 0;
}
// create a new file over the orphan
lfsr_file_open(&lfs, &file, "gello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
=> strlen("hello!");
lfsr_file_close(&lfs, &file) => 0;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
// 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, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
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_, "gello", LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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, "fello%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, "gello",
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, CFG) => 0;
}
// create a new dir over the orphan
lfsr_mkdir(&lfs, "gello") => 0;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_remove]
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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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, "fello%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, "gello",
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, CFG) => 0;
}
// orphans aren't real, so remove should fail
lfsr_remove(&lfs, "gello") => LFS_ERR_NOENT;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_rename_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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/iello" : "iello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/iello" : "iello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
=> strlen("hello!");
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/fello%03x" : "fello%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/gello" : "gello",
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, CFG) => 0;
}
// rename onto orphan
lfsr_rename(&lfs,
(INTERDIR) ? "a/iello" : "iello",
(INTERDIR) ? "c/gello" : "gello") => 0;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "c/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/iello" : "iello", &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, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "hello%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/gello" : "gello",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "c/gello" : "gello",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_forphans_orphan_rename_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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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, "fello%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, "gello",
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, CFG) => 0;
}
// orphans aren't real, so rename should fail
lfsr_rename(&lfs, "gello", "hello") => LFS_ERR_NOENT;
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "hello", &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 = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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, "gello") => 0;
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
// even after a remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
// via stat
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_forphans_zombie_posthumous]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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, "gello") => 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;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
// even after a remount
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
// via stat
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", LFS_O_RDWR) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_forphans_zombie_rwrw]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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, "gello") => 0;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "gello",
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;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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 = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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, "gello") => 0;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "gello",
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;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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 = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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, "gello") => 0;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "gello",
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, "gello") => 0;
// create a third file
lfsr_file_t file___;
lfsr_file_open(&lfs, &file___, "gello",
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;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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 = [
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.CHUNK = 'LFS_MIN(64, SIZE)'
defines.SYNC = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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, "gello") => 0;
// create a second file
lfsr_file_t file__;
lfsr_file_open(&lfs, &file__, "gello",
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, "gello") => 0;
// create a third file
lfsr_file_t file___;
lfsr_file_open(&lfs, &file___, "gello",
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;
}
// as far as the filesystem is concerned, the file does not exist yet
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_, "gello", LFS_O_RDONLY) => LFS_ERR_NOENT;
// non-create rejected
lfsr_file_open(&lfs, &file_, "gello", LFS_O_WRONLY) => LFS_ERR_NOENT;
lfsr_file_open(&lfs, &file_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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, CFG) => 0;
// check disk again again
// via stat
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_, "gello", 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]
# 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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "gello",
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, "gello") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create a new file over the zombie
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
=> strlen("hello!");
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, CFG) => 0;
}
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
// 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, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
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_, "gello", LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 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]
# 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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "gello",
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, "gello") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create a new dir over the zombie
lfsr_mkdir(&lfs, "gello") => 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, CFG) => 0;
}
}
// make sure the new dir is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_remove]
# 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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "gello",
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, "gello") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// zombies aren't real, so remove should fail
lfsr_remove(&lfs, "gello") => 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, CFG) => 0;
}
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_rename_dst]
defines.DIR = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
# 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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/iello" : "iello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/iello" : "iello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
=> strlen("hello!");
lfsr_file_close(&lfs, &file) => 0;
}
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/gello" : "gello",
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/gello" : "gello") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// rename onto zombie
lfsr_rename(&lfs,
(INTERDIR) ? "c/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 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, CFG) => 0;
}
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/iello" : "iello", &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, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "hello%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/gello" : "gello",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/gello" : "gello",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_forphans_zombie_rename_src]
# 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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "gello",
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, "gello") => 0;
if (CLOSE == 2) {
lfsr_file_close(&lfs, &zombie) => 0;
}
if (REMOUNT == 2) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// zombies aren't real, so rename should fail
lfsr_rename(&lfs, "gello", "hello") => 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, CFG) => 0;
}
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "hello", &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.CLOSE = [false, true]
defines.REMOUNT = [false, true]
defines.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file, not a zombie yet
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "gello",
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, "gello") => 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, CFG) => 0;
}
// make sure the new dir is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 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, "gello") == 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_rename_dst]
defines.ORPHAN = [false, true]
defines.DIR = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
defines.CLOSE = [false, true]
defines.REMOUNT = [false, true]
defines.POSTHUMOUS = [false, true]
if = [
'REMOUNT <= CLOSE',
'!DIR || ORPHAN',
]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/iello" : "iello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/iello" : "iello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
=> strlen("hello!");
lfsr_file_close(&lfs, &file) => 0;
}
// create a file, not a zombie yet
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/gello" : "gello",
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/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 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, CFG) => 0;
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/iello" : "iello", &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, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, "hello%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/gello" : "gello",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/gello" : "gello",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 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_remove]
defines.DIR = [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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, "gello",
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, "gello") => 0;
// create a file on top of the zombie
if (DIR) {
lfsr_mkdir(&lfs, "gello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "gello",
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, CFG) => 0;
}
// remove the file
lfsr_remove(&lfs, "gello") => 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, CFG) => 0;
}
}
// just make sure things look ok
// via stat
struct lfs_info info;
lfsr_stat(&lfs, "gello", &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_rename_dst]
defines.DIR = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
# 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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/iello" : "iello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/iello" : "iello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
=> strlen("hello!");
lfsr_file_close(&lfs, &file) => 0;
}
// create a zombie
lfsr_file_t zombie;
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/gello" : "gello",
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/gello" : "gello") => 0;
// create a file on top of the zombie
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "a/gello" : "gello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/gello" : "gello",
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, CFG) => 0;
}
// rename onto the file
lfsr_rename(&lfs,
(INTERDIR) ? "c/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 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, CFG) => 0;
}
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "a/iello" : "iello", &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, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/hello%03x" : "hello%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/gello" : "gello",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/gello" : "gello",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# these doesn't really involve scratch files, but we might as well test
# them here
[cases.test_forphans_rename]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/gello" : "gello",
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/iello" : "iello",
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/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 0;
// the file should have been renamed
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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_rename_postrename]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/gello" : "gello",
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/iello" : "iello",
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/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 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;
}
// the file should have been renamed, but zero sized
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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_rename_rwrw]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/gello" : "gello",
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/iello" : "iello",
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/iello" : "iello",
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/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 0;
// the file should have been renamed
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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_rename_rwrw_postrename]
defines.SIZE = [
'FBUFFER_SIZE/2',
'2*FBUFFER_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]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/gello" : "gello",
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/iello" : "iello",
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/iello" : "iello",
LFS_O_RDWR | LFS_O_TRUNC) => 0;
// rename the file
lfsr_rename(&lfs,
(INTERDIR) ? "c/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 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;
}
// the file should have been renamed, but zero sized
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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, CFG) => 0;
// remount should have no effect
// via stat
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 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/hello%03x" : "hello%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/gello" : "gello",
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_rename_rename_src]
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
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.POSTRENAME = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/gello" : "gello",
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/iello" : "iello",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_sync(&lfs, &file) => 0;
if (!POSTRENAME) {
lfsr_file_write(&lfs, &file,
"hello!", strlen("hello!"))
=> strlen("hello!");
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, CFG) => 0;
}
// rename while open
lfsr_rename(&lfs,
(INTERDIR) ? "c/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 0;
if (CLOSE <= 1 && REMOUNT <= 1) {
if (POSTRENAME) {
lfsr_file_write(&lfs, &file,
"hello!", strlen("hello!"))
=> strlen("hello!");
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("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
if (CLOSE == 1) {
lfsr_file_close(&lfs, &file) => 0;
}
if (REMOUNT == 1) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
if ((SYNC || CLOSE >= 1) && !(POSTRENAME && CLOSE >= 2)) {
assert(info.size == strlen("hello!"));
} else {
assert(info.size == 0);
}
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 0);
assert(info.type == LFS_TYPE_REG);
if ((SYNC || CLOSE >= 1) && !(POSTRENAME && CLOSE >= 2)) {
assert(info.size == strlen("hello!"));
} else {
assert(info.size == 0);
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/hello%03x" : "hello%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/gello" : "gello",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
if ((SYNC || CLOSE >= 1) && !(POSTRENAME && CLOSE >= 2)) {
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 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_rename_src]
defines.DIR = [false, true]
defines.EXISTS = [false, true]
defines.INTERDIR = [false, true]
defines.DISTANCE = [0, 1, 100]
# 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.POSTHUMOUS = [false, true]
if = 'REMOUNT <= CLOSE'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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/hello%03x" : "hello%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/gello" : "gello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/gello" : "gello",
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/iello" : "iello",
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/iello" : "iello") => 0;
// create a file on top of the zombie
if (DIR) {
lfsr_mkdir(&lfs, (INTERDIR) ? "c/iello" : "iello") => 0;
} else {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/iello" : "iello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file, "hello!", strlen("hello!"))
=> strlen("hello!");
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, CFG) => 0;
}
// rename while open
lfsr_rename(&lfs,
(INTERDIR) ? "c/iello" : "iello",
(INTERDIR) ? "a/gello" : "gello") => 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, CFG) => 0;
}
}
// make sure the new file is readable
// via stat
struct lfs_info info;
lfsr_stat(&lfs, (INTERDIR) ? "a/gello" : "gello", &info) => 0;
assert(strcmp(info.name, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
lfsr_stat(&lfs, (INTERDIR) ? "c/iello" : "iello", &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, "gello") == 0);
if (DIR) {
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
} else {
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hello!"));
}
if (!INTERDIR) {
for (lfs_size_t i = 0; i < DISTANCE; i++) {
char name[256];
sprintf(name, (INTERDIR) ? "a/hello%03x" : "hello%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/gello" : "gello",
LFS_O_RDONLY) => LFS_ERR_ISDIR;
} else {
lfsr_file_t file_;
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/gello" : "gello",
LFS_O_RDONLY) => 0;
uint8_t rbuf[256];
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("hello!");
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
lfsr_file_close(&lfs, &file_) => 0;
}
if (CLOSE == 0) {
lfsr_file_close(&lfs, &zombie) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# fuzz tests involving many orphans + zombies, this gets a bit crazy
[cases.test_forphans_orphanzombie_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64]
# do more ops than files to encourage file rewrites
defines.DENSITY = 2
defines.SIZE = [
'0',
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.SEED = 'range(20)'
if = '(SIZE*N)/BLOCK_SIZE <= 32'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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 orphan;
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 < N; 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+DENSITY-1) / DENSITY)) {
goto nonsense;
}
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
// already exists?
bool orphan = true;
uint32_t wprng = 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
if (sim[j] == x) {
orphan = false;
wprng = sim_prngs[j];
break;
}
}
// choose a random seed if we don't exist
if (orphan) {
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]->orphan = orphan;
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 (orphan) {
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]->orphan = 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+DENSITY-1) / DENSITY);
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 orphans + zombies + dirs, this gets a bit crazy
[cases.test_forphans_orphanzombiedir_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64]
# do more ops than files to encourage file rewrites
defines.DENSITY = 2
defines.SIZE = [
'0',
'FBUFFER_SIZE/2',
'2*FBUFFER_SIZE',
'BLOCK_SIZE/2',
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'4*BLOCK_SIZE',
]
defines.SEED = 'range(20)'
if = '(SIZE*N)/BLOCK_SIZE <= 32'
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, 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 orphan;
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 < N; 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+DENSITY-1) / DENSITY)) {
goto nonsense;
}
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
// already exists?
bool orphan = 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;
}
orphan = false;
wprng = sim_prngs[j];
break;
}
}
// choose a random seed if we don't exist
if (orphan) {
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]->orphan = orphan;
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 (orphan) {
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]->orphan = 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+DENSITY-1) / DENSITY);
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+DENSITY-1) / DENSITY);
// 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;
'''