Files
littlefs/tests/test_forphan.toml
T
Christopher Haster 186fd1b5f2 Separated cache_size out into rcache_size/pcache_size/fbuffer_size
A much requested feature, this allows much finer control of how RAM is
allocated for the system.

It was difficult to introduce this in previous versions of littlefs due
to how we steal caches during certain file operations, but now we don't
do that and treat the caches much more transparently.

Managing separate cache sizes does add a bit of code, but this is well
worth the potential for RAM savings due to increased flexibility:

           code          stack
  before: 33656           2632
  after:  33714 (+0.2%)   2640 (+0.3%)

Also interesting to note this reduces alignment requirements for the
rcache/pcache, since they don't need to share alignment, and completely
removes any alignment requirement from the file buffers.
2024-05-22 15:43:10 -05:00

5945 lines
195 KiB
TOML

# Test orphaned files and their various use cases
after = ['test_fwrite', 'test_fsync']
# Some specific tests
[cases.test_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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_forphan_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;
'''