acfae9e072
This has been a long-time coming, mount flags are just too useful for
configuring a filesystem at runtime.
Currently this is limited to LFS_M_RDONLY and LFS_M_CKPROGS, but there
are a few more planned in the future:
LFS_M_RDWR = 0x0000, // Mount the filesystem as read and write
LFS_M_RDONLY = 0x0001, // Mount the filesystem as readonly
LFS_M_STRICT* = 0x0002, // Error if on-disk config does not match
LFS_M_FORCE* = 0x0004, // Ignore compat flags, mount readonly
LFS_M_FORCEWITHRECKLESSABANDON*
= 0x0008, // Ignore compat flags, mount read write
LFS_M_CKPROGS = 0x0010, // Check progs by reading back progged data
LFS_M_CKREADS* = 0x0020, // Check reads via checksums
* Hypothetical
As a convenience, we also return mount flags in the struct lfs_fsinfo's
flags field as their relevant LFS_I_* variants. Though only to match
statvfs, and only because it's cheap, littlefs's API is low-level and we
should expect users to know what flags they passed to lfsr_mount.
As for the new mount flags:
- LFS_M_RDONLY - For consistency with existing APIs, this just asserts
on write operations, which makes it a bit useless... But the info flag
LFS_I_RDONLY may be useful for falling back to a readonly mode if
we encounter on-disk compat issues.
At least if implement the theoretical LFS_UNTRUSTED_USER mode
LFS_M_RDONLY could become a runtime error.
- LFS_M_RDWR - This really just exists to compliment LFS_M_RDONLY and to
match LFS_O_RDONLY/LFS_O_RDWR. It's just an alias for 0, and I don't
think there will ever be a reason to make it non-0 (but I can always
be wrong!).
- LFS_M_CKPROGS - This replaces the check_progs config option and avoids
using a full byte to store a bool.
We should probably also have a compile-time option to compile this out
(LFS_NO_CKPROGS?), but that's a future thing to do.
This ended up adding a surprising bit of code, considering we're just
moving flags around, and noise in lfs_alloc added a bit of stack again:
code stack
before: 35880 2672
after: 35932 (+0.1%) 2680 (+0.3%)
8106 lines
266 KiB
TOML
8106 lines
266 KiB
TOML
# Test orphaned files and their various use cases
|
|
after = ['test_fwrite', 'test_fsync']
|
|
|
|
|
|
# Some specific tests
|
|
[cases.test_forphans_create]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.SYNC = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// check that the file doesn't _really_ exist
|
|
lfs_t lfs_;
|
|
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs_, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs_, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
lfsr_unmount(&lfs_) => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// check that the file still doesn't _really_ exist
|
|
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
|
|
// via stat
|
|
lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs_, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs_, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
lfsr_unmount(&lfs_) => 0;
|
|
}
|
|
|
|
if (SYNC) {
|
|
// sync the file
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// now it should show up
|
|
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
|
|
// via stat
|
|
lfsr_stat(&lfs_, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs_, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs_, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs_, &file_) => 0;
|
|
lfsr_unmount(&lfs_) => 0;
|
|
}
|
|
|
|
// close the file
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// now it should show up
|
|
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
|
|
// via stat
|
|
lfsr_stat(&lfs_, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs_, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs_, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs_, &file_) => 0;
|
|
lfsr_unmount(&lfs_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
|
|
// should still be there
|
|
lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0;
|
|
// via stat
|
|
lfsr_stat(&lfs_, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs_, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs_, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs_, &file_) => 0;
|
|
lfsr_unmount(&lfs_) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_create_pl]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
reentrant = true
|
|
code = '''
|
|
// format once per test
|
|
lfs_t lfs;
|
|
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
|
|
if (err) {
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// create a file
|
|
//
|
|
// note the excl flag
|
|
lfsr_file_t file;
|
|
err = lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL);
|
|
assert(!err || err == LFS_ERR_EXIST);
|
|
|
|
if (err != LFS_ERR_EXIST) {
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// close the file
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// we should be able to read the file now
|
|
lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0;
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// and after remounting
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_create_many_pl]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
]
|
|
defines.CHUNK = 8
|
|
defines.N = 128
|
|
reentrant = true
|
|
code = '''
|
|
// format once per test
|
|
lfs_t lfs;
|
|
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
|
|
if (err) {
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// create N files
|
|
for (lfs_size_t j = 0; j < N; j++) {
|
|
// note the excl flag
|
|
char name[256];
|
|
sprintf(name, "batman%03x", j);
|
|
lfsr_file_t file;
|
|
err = lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL);
|
|
assert(!err || err == LFS_ERR_EXIST);
|
|
|
|
if (err != LFS_ERR_EXIST) {
|
|
// write to the file
|
|
uint32_t prng = 42 + j;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// close the file
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// we should be able to read the files now
|
|
for (lfs_size_t j = 0; j < N; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", j);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
uint32_t prng = 42 + j;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// and after remounting
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < N; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", j);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
uint32_t prng = 42 + j;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_create_sync_wr]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.SYNC = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// but we should still recieve sync broadcasts on sync/close
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
if (SYNC) {
|
|
// sync the file
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close the file
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_create_sync_rw]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.SYNC = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
// open a second reference
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// write to the second file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
if (SYNC) {
|
|
// sync the second file
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close the second file
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_create_desync_wdwr]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.SYNC = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a desync file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
// open a second reference
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
// and a third for checking sync broadcasts
|
|
lfsr_file_t file___;
|
|
lfsr_file_open(&lfs, &file___, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// write to the first file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// write to the second file
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
if (SYNC) {
|
|
// sync the second file
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close the second file
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// now sync the first file, this should overwrite what is written
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// and close, this shouldn't change anything
|
|
//
|
|
// note we must sync to clear the desync flag
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file___) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a desync file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// because the file was desynced, it should still not exist
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// even after a remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// because the file was desynced, it should still not exist
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan_wdwr]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
# SYNC=0x1 => sync before orphaning
|
|
# SYNC=0x2 => sync after orphaning
|
|
defines.SYNC = [0, 1, 2, 3]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a desync file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
// open a second reference
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
// and a third for checking sync broadcasts
|
|
lfsr_file_t file___;
|
|
lfsr_file_open(&lfs, &file___, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// write to the first file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// write to the second file
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
if (SYNC & 0x1) {
|
|
// sync the second file
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close the first file, this should do nothing but discard the
|
|
// file contents
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (SYNC & 0x2) {
|
|
// sync the second file
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close the second file
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
|
|
// now it should show up
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// recieved sync broadcast?
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_close(&lfs, &file___) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan_unrelated]
|
|
# different number of orphan require different methods of cleanup
|
|
defines.N = 'range(6)'
|
|
defines.M = 'range(6)'
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create N orphans
|
|
lfsr_file_t orphans[N];
|
|
for (lfs_size_t o = 0; o < N; o++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", o);
|
|
lfsr_file_open(&lfs, &orphans[o], name,
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
uint32_t prng = 42+o;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &orphans[o], wbuf, CHUNK) => CHUNK;
|
|
}
|
|
}
|
|
|
|
// and an unrelated file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint32_t prng = 52;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, none of the orphans exist
|
|
// via stat
|
|
struct lfs_info info;
|
|
for (lfs_size_t o = 0; o < N; o++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", o);
|
|
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
|
|
}
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
for (lfs_size_t o = 0; o < N; o++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", o);
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, name, LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, name, LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, name, LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// but we should still be able to read our orphans
|
|
for (lfs_size_t o = 0; o < N; o++) {
|
|
lfsr_file_rewind(&lfs, &orphans[o]) => 0;
|
|
prng = 42+o;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &orphans[o], rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 52;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close M orphans
|
|
for (lfs_size_t o = 0; o < M && o < N; o++) {
|
|
lfsr_file_close(&lfs, &orphans[o]) => 0;
|
|
}
|
|
|
|
// create a new unrelated file, this should trigger
|
|
// and orphan cleanup
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "catman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
prng = 62;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
lfsr_file_close(&lfs, &file__);
|
|
|
|
// and now close our original unrelated file
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// and close the remaining orphans
|
|
for (lfs_size_t o = M; o < N; o++) {
|
|
lfsr_file_close(&lfs, &orphans[o]) => 0;
|
|
}
|
|
|
|
// now our file should exist, but none of our orphans
|
|
// via stat
|
|
for (lfs_size_t o = 0; o < N; o++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", o);
|
|
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
|
|
}
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, "catman", &info) => 0;
|
|
assert(strcmp(info.name, "catman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "catman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
for (lfs_size_t o = 0; o < N; o++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", o);
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, name, LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, name, LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, name, LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
}
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 52;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0;
|
|
prng = 62;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan_open]
|
|
defines.ORPHANS = [1, 2, 3, 100]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create neighboring orphaned files
|
|
//
|
|
// more orphans requires different techniques for cleaning up orphans
|
|
lfsr_file_t orphans[ORPHANS-1];
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", i);
|
|
lfsr_file_open(&lfs, &orphans[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &orphans[i],
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// create an orphaned file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
|
|
// close all orphans at once, or else the open calls would just
|
|
// clean up each orphans
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// create a new file over the orphan
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan_mkdir]
|
|
defines.ORPHANS = [1, 2, 3, 100]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create neighboring orphaned files
|
|
//
|
|
// more orphans requires different techniques for cleaning up orphans
|
|
lfsr_file_t orphans[ORPHANS-1];
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", i);
|
|
lfsr_file_open(&lfs, &orphans[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &orphans[i],
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// create an orphaned file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
|
|
// close all orphans at once, or else the open calls would just
|
|
// clean up each orphans
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// create a new dir over the orphan
|
|
lfsr_mkdir(&lfs, "batman") => 0;
|
|
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new dir is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan_rm]
|
|
defines.ORPHANS = [1, 2, 3, 100]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create neighboring orphaned files
|
|
//
|
|
// more orphans requires different techniques for cleaning up orphans
|
|
lfsr_file_t orphans[ORPHANS-1];
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", i);
|
|
lfsr_file_open(&lfs, &orphans[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &orphans[i],
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// create an orphaned file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
|
|
// close all orphans at once, or else the open calls would just
|
|
// clean up each orphans
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// orphans aren't real, so remove should fail
|
|
lfsr_remove(&lfs, "batman") => LFS_ERR_NOENT;
|
|
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// just make sure things look ok
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan_mv_dst]
|
|
defines.DIR = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.ORPHANS = [1, 2, 3, 100]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// make our src file before orphans, otherwise open just cleans
|
|
// things up
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, (INTERDIR) ? "a/datman" : "datman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/datman" : "datman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create neighboring orphaned files
|
|
//
|
|
// more orphans requires different techniques for cleaning up orphans
|
|
lfsr_file_t orphans[ORPHANS-1];
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "c/aatman%03x" : "aatman%03x", i);
|
|
lfsr_file_open(&lfs, &orphans[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &orphans[i],
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// create an orphaned file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
|
|
// close all orphans at once, or else the open calls would just
|
|
// clean up each orphans
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// rename onto orphan
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "a/datman" : "datman",
|
|
(INTERDIR) ? "c/batman" : "batman") => 0;
|
|
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "c" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
if (DIR) {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "c/batman" : "batman",
|
|
LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
} else {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "c/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_orphan_mv_src]
|
|
defines.ORPHANS = [1, 2, 3, 100]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create neighboring orphaned files
|
|
//
|
|
// more orphans requires different techniques for cleaning up orphans
|
|
lfsr_file_t orphans[ORPHANS-1];
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
char name[256];
|
|
sprintf(name, "aatman%03x", i);
|
|
lfsr_file_open(&lfs, &orphans[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &orphans[i],
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// create an orphaned file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
lfsr_file_write(&lfs, &file,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
|
|
// close all orphans at once, or else the open calls would just
|
|
// clean up each orphans
|
|
for (lfs_size_t i = 0; i < ORPHANS-1; i++) {
|
|
lfsr_file_close(&lfs, &orphans[i]) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// orphans aren't real, so rename should fail
|
|
lfsr_rename(&lfs, "batman", "catman") => LFS_ERR_NOENT;
|
|
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// just make sure things look ok
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
lfsr_stat(&lfs, "catman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// removed files can not be synced
|
|
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
|
|
|
|
// but we should still be able to read our file handle
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// because the file was removed, it should still not exist
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// even after a remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_posthumous]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// removed files can not be synced
|
|
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
|
|
|
|
// but we should still be able to read our file handle
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// because the file was removed, it should still not exist
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// even after a remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_rwrw]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// create a second file
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the second file
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// removed files can not be synced
|
|
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
|
|
// but we should be able to sync our second file just fine
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// second file should appear on disk now
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// the perhaps surprising thing is we should still be able
|
|
// to read both file handles
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
|
|
// check disk again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// check disk again again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_rwrw_posthumous]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// create a second file
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the second file
|
|
uint32_t prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// write to the first file
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// removed files can not be synced
|
|
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
|
|
// but we should be able to sync our second file just fine
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// second file should appear on disk now
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// the perhaps surprising thing is we should still be able
|
|
// to read both file handles
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
|
|
// check disk again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// check disk again again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_zombie_rwrwrw]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// create a second file
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the second file
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// create a third file
|
|
lfsr_file_t file___;
|
|
lfsr_file_open(&lfs, &file___, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the third file
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file___, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// removed files can not be synced
|
|
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
|
|
lfsr_file_sync(&lfs, &file__) => LFS_ERR_NOENT;
|
|
// but we should be able to sync our third file just fine
|
|
lfsr_file_sync(&lfs, &file___) => 0;
|
|
|
|
// third file should appear on disk now
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// the perhaps surprising thing is we should still be able
|
|
// to read all file handles
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
lfsr_file_close(&lfs, &file___) => 0;
|
|
|
|
// check disk again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// check disk again again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_zombie_rwrwrw_posthumous]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// create a second file
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// need to sync so we can remove
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// create a third file
|
|
lfsr_file_t file___;
|
|
lfsr_file_open(&lfs, &file___, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the third file
|
|
uint32_t prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file___, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// write to the second file
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// write to the first file
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// as far as the filesystem is concerned, the file does not exist yet
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// rdonly rejected
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
// non-create rejected
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT;
|
|
|
|
// removed files can not be synced
|
|
lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT;
|
|
lfsr_file_sync(&lfs, &file__) => LFS_ERR_NOENT;
|
|
// but we should be able to sync our third file just fine
|
|
lfsr_file_sync(&lfs, &file___) => 0;
|
|
|
|
// third file should appear on disk now
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// the perhaps surprising thing is we should still be able
|
|
// to read all file handles
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file___) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
lfsr_file_close(&lfs, &file___) => 0;
|
|
|
|
// check disk again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// check disk again again
|
|
// via stat
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
prng = 42+2;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_open]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// create a new file over the zombie
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_mkdir]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// create a new dir over the zombie
|
|
lfsr_mkdir(&lfs, "batman") => 0;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new dir is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_rm]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// zombies aren't real, so remove should fail
|
|
lfsr_remove(&lfs, "batman") => LFS_ERR_NOENT;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// just make sure things look ok
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_mv_dst]
|
|
defines.DIR = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// make our src file before zombies, just in case
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// rename onto zombie
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
if (DIR) {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
} else {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
}
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombie_mv_src]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// zombies aren't real, so rename should fail
|
|
lfsr_rename(&lfs, "batman", "catman") => LFS_ERR_NOENT;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// just make sure things look ok
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
lfsr_stat(&lfs, "catman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombify_mkdir]
|
|
defines.POSTHUMOUS = [false, true]
|
|
defines.CLOSE = [false, true]
|
|
defines.REMOUNT = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a file, not a zombie yet
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// use mkdir on the same name, the file hasn't been created yet,
|
|
// so this shouldn't fail, but because we have an open file handle
|
|
// we create a zombie
|
|
lfsr_mkdir(&lfs, "batman") => 0;
|
|
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new dir is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
if (!CLOSE) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_zombify_mv_dst]
|
|
defines.ORPHAN = [false, true]
|
|
defines.DIR = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.POSTHUMOUS = [false, true]
|
|
defines.CLOSE = [false, true]
|
|
defines.REMOUNT = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = [
|
|
'REMOUNT <= CLOSE',
|
|
'!DIR || ORPHAN',
|
|
]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// make our src file before zombies, just in case
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a file, not a zombie yet
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
if (!ORPHAN) {
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
}
|
|
|
|
// rename onto the same name, this shouldn't fail (note the test
|
|
// conditions), but because we have an open file handle we create
|
|
// a zombie
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
if (DIR) {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
} else {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
}
|
|
|
|
if (!CLOSE) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_file_on_zombie_rm]
|
|
defines.DIR = [false, true]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
// create a file on top of the zombie
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, "batman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// remove the file
|
|
lfsr_remove(&lfs, "batman") => 0;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// just make sure things look ok
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_file_on_zombie_mv_dst]
|
|
defines.DIR = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// make our src file before zombies, just in case
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
// create a file on top of the zombie
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// rename onto the file
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our zombie
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
if (DIR) {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
} else {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
}
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# test that we actually cleanup orphans correctly
|
|
[cases.test_forphans_cleanup]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.N = [0, 1, 2, 3, 10, 100]
|
|
defines.REMOUNT = [false, true]
|
|
defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create some unrelated files to make sure cleaning up orphans doesn't
|
|
// break other filesystem things
|
|
uint32_t bookend_prngs[2] = {0, 0};
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "aatman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
bookend_prngs[0] = TEST_PRNG(&prng);
|
|
uint32_t prng_ = bookend_prngs[0];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "catman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
bookend_prngs[1] = TEST_PRNG(&prng);
|
|
uint32_t prng_ = bookend_prngs[1];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfsr_file_t files[N];
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", i);
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
// remount? this has no effect on orphans
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// calling lfsr_fs_mkconsistent should clean things up
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs.grm.mids[0] == -1);
|
|
assert(lfs.grm.mids[1] == -1);
|
|
assert(!(lfs.flags & LFS_F_ORPHANS));
|
|
|
|
struct lfs_fsinfo fsinfo;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS_I_INCONSISTENT));
|
|
|
|
// double check the actual disk state, it's easy for littlefs to
|
|
// lie here
|
|
assert(lfsr_mtree_weight(&lfs)
|
|
<= ((1+lfs_popc(BOOKENDS)) << lfs.mdir_bits));
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight <= 1+lfs_popc(BOOKENDS));
|
|
|
|
// check that other files are unaffected
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
if (remount) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "aatman", LFS_O_RDONLY) => 0;
|
|
uint32_t prng_ = bookend_prngs[0];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file);
|
|
}
|
|
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "catman", LFS_O_RDONLY) => 0;
|
|
uint32_t prng_ = bookend_prngs[1];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file);
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_cleanup_opened]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.N = [0, 1, 2, 3, 10, 100]
|
|
defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create some unrelated files to make sure cleaning up orphans doesn't
|
|
// break other filesystem things
|
|
lfsr_file_t bookend_files[2];
|
|
uint32_t bookend_prngs[2] = {0, 0};
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
bookend_prngs[0] = TEST_PRNG(&prng);
|
|
uint32_t prng_ = bookend_prngs[0];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &bookend_files[0], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &bookend_files[0]) => 0;
|
|
}
|
|
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_open(&lfs, &bookend_files[1], "catman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
bookend_prngs[1] = TEST_PRNG(&prng);
|
|
uint32_t prng_ = bookend_prngs[1];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &bookend_files[1], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &bookend_files[1]) => 0;
|
|
}
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfsr_file_t files[N];
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", i);
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
// calling lfsr_fs_mkconsistent should clean things up
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs.grm.mids[0] == -1);
|
|
assert(lfs.grm.mids[1] == -1);
|
|
assert(!(lfs.flags & LFS_F_ORPHANS));
|
|
|
|
struct lfs_fsinfo fsinfo;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS_I_INCONSISTENT));
|
|
|
|
// double check the actual disk state, it's easy for littlefs to
|
|
// lie here
|
|
assert(lfsr_mtree_weight(&lfs)
|
|
<= ((1+lfs_popc(BOOKENDS)) << lfs.mdir_bits));
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight <= 1+lfs_popc(BOOKENDS));
|
|
|
|
// check that other files are unaffected
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
if (remount) {
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_close(&lfs, &bookend_files[0]) => 0;
|
|
}
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_close(&lfs, &bookend_files[1]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
|
|
LFS_O_RDONLY) => 0;
|
|
}
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_open(&lfs, &bookend_files[1], "catman",
|
|
LFS_O_RDONLY) => 0;
|
|
}
|
|
}
|
|
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_rewind(&lfs, &bookend_files[0]) => 0;
|
|
uint32_t prng_ = bookend_prngs[0];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &bookend_files[0], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_rewind(&lfs, &bookend_files[1]) => 0;
|
|
uint32_t prng_ = bookend_prngs[1];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &bookend_files[1], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_close(&lfs, &bookend_files[0]);
|
|
}
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_close(&lfs, &bookend_files[1]);
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_cleanup_orphaned]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# <=2 => grm-able
|
|
# >2 => requires orphans
|
|
defines.N = [0, 1, 2, 3, 10, 100]
|
|
defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3]
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
|
|
// create some unrelated files to make sure cleaning up orphans doesn't
|
|
// break other filesystem things
|
|
lfsr_file_t bookend_files[2];
|
|
uint32_t bookend_prngs[2] = {0, 0};
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
bookend_prngs[0] = TEST_PRNG(&prng);
|
|
uint32_t prng_ = bookend_prngs[0];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &bookend_files[0], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_open(&lfs, &bookend_files[1], "catman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
bookend_prngs[1] = TEST_PRNG(&prng);
|
|
uint32_t prng_ = bookend_prngs[1];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &bookend_files[1], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// create this many orphaned files
|
|
//
|
|
// anytime we close a not-yet-created desync file, we create an
|
|
// orphan, but note we need these to be different files, and we need
|
|
// to close them after all open calls, otherwise we just end up with
|
|
// one orphan (littlefs is eager to clean up orphans)
|
|
//
|
|
lfsr_file_t files[N];
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", i);
|
|
lfsr_file_open(&lfs, &files[i], name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
// calling lfsr_fs_mkconsistent should clean things up
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
|
|
// we should have cleaned up all grms/orphans
|
|
assert(lfs.grm.mids[0] == -1);
|
|
assert(lfs.grm.mids[1] == -1);
|
|
assert(!(lfs.flags & LFS_F_ORPHANS));
|
|
|
|
struct lfs_fsinfo fsinfo;
|
|
lfsr_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(!(fsinfo.flags & LFS_I_INCONSISTENT));
|
|
|
|
// double check the actual disk state, it's easy for littlefs to
|
|
// lie here
|
|
assert(lfsr_mtree_weight(&lfs)
|
|
<= ((1+lfs_popc(BOOKENDS)) << lfs.mdir_bits));
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight <= 1+lfs_popc(BOOKENDS));
|
|
|
|
// check that other files are unaffected
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
if (remount) {
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_close(&lfs, &bookend_files[0]) => 0;
|
|
}
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_close(&lfs, &bookend_files[1]) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_open(&lfs, &bookend_files[0], "aatman",
|
|
LFS_O_RDONLY) => 0;
|
|
}
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_open(&lfs, &bookend_files[1], "catman",
|
|
LFS_O_RDONLY) => 0;
|
|
}
|
|
}
|
|
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_rewind(&lfs, &bookend_files[0]) => 0;
|
|
uint32_t prng_ = bookend_prngs[0];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &bookend_files[0], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_rewind(&lfs, &bookend_files[1]) => 0;
|
|
uint32_t prng_ = bookend_prngs[1];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &bookend_files[1], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
if (BOOKENDS & 0x1) {
|
|
lfsr_file_close(&lfs, &bookend_files[0]);
|
|
}
|
|
if (BOOKENDS & 0x2) {
|
|
lfsr_file_close(&lfs, &bookend_files[1]);
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# these doesn't really involve scratch files, but we might as well test
|
|
# them here
|
|
[cases.test_forphans_mv]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.EXISTS = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create our destination first, just in case
|
|
if (EXISTS) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// need to sync so we can rename
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// the file should have been renamed
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// but we should still be able to read our file handle
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// close should have no effect
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// remount should have no effect
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_mv_postmv]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.EXISTS = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.SYNC = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create our destination first, just in case
|
|
if (EXISTS) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// need to sync so we can rename
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// the file should have been renamed, but zero sized
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// but we should still be able to read our file handle
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
if (SYNC) {
|
|
// sync
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// the data should now be visible
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info)
|
|
=> LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// we should still be able to read our file handle
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// the data should now be visible
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// remount should have no effect
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_mv_rwrw]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.EXISTS = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.SYNC = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create our destination first, just in case
|
|
if (EXISTS) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// write to the file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// need to sync so we can rename
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// create a second file
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_TRUNC) => 0;
|
|
|
|
// write to the second file
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// the file should have been renamed
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// but we should still be able to read both file handles
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
if (SYNC) {
|
|
// sync, note the order
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// the second file's data should now be visible
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info)
|
|
=> LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// both file handles should be updated
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close, note the order
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// close should have no effect
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// remount should have no effect
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_mv_rwrw_postmv]
|
|
defines.SIZE = [
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = 'LFS_MIN(64, SIZE)'
|
|
defines.EXISTS = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.SYNC = [false, true]
|
|
defines.MKCONSISTENT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create our destination first, just in case
|
|
if (EXISTS) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
|
|
// need to sync so we can rename
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// create a second file
|
|
lfsr_file_t file__;
|
|
lfsr_file_open(&lfs, &file__, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_TRUNC) => 0;
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
// write to the first file
|
|
uint32_t prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// write to the second file
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK;
|
|
}
|
|
|
|
// mkconsistent should have no effect
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// the file should have been renamed, but zero sized
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0;
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// but we should still be able to read both file handles
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
if (SYNC) {
|
|
// sync, note the order
|
|
lfsr_file_sync(&lfs, &file__) => 0;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
|
|
// the second file's data should now be visible
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info)
|
|
=> LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// both file handles should be updated
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &file__) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
}
|
|
|
|
// close, note the order
|
|
lfsr_file_close(&lfs, &file__) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// close should have no effect
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
// remount
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// remount should have no effect
|
|
// via stat
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
prng = 42+1;
|
|
for (lfs_off_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(wbuf, rbuf, CHUNK) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_mv_mv_src]
|
|
defines.EXISTS = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.POSTMV = [false, true]
|
|
defines.SYNC = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create our destination first, just in case
|
|
if (EXISTS) {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
if (!POSTMV) {
|
|
lfsr_file_write(&lfs, &file,
|
|
"catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// rename while open
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTMV) {
|
|
lfsr_file_write(&lfs, &file,
|
|
"catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
if (SYNC) {
|
|
lfsr_file_sync(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// we should still be able to read our file
|
|
lfsr_file_rewind(&lfs, &file) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf))
|
|
=> strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
|
|
assert(info.size == strlen("catman!"));
|
|
} else {
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
|
|
assert(info.size == strlen("catman!"));
|
|
} else {
|
|
assert(info.size == 0);
|
|
}
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) {
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
} else {
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0;
|
|
}
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_file_on_zombie_mv_src]
|
|
defines.DIR = [false, true]
|
|
defines.EXISTS = [false, true]
|
|
defines.INTERDIR = [false, true]
|
|
defines.DISTANCE = [0, 1, 100]
|
|
defines.POSTHUMOUS = [false, true]
|
|
# CLOSE=0 => don't close (before end of test)
|
|
# CLOSE=1 => close after op
|
|
# CLOSE=2 => close before op
|
|
defines.CLOSE = [0, 1, 2]
|
|
# REMOUNT=0 => don't remount
|
|
# REMOUNT=1 => remount after op
|
|
# REMOUNT=2 => remount before op
|
|
defines.REMOUNT = [0, 1, 2]
|
|
defines.MKCONSISTENT = [false, true]
|
|
if = 'REMOUNT <= CLOSE'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// create an interesting directory structure
|
|
if (INTERDIR) {
|
|
lfsr_mkdir(&lfs, "a") => 0;
|
|
lfsr_mkdir(&lfs, "b") => 0;
|
|
lfsr_mkdir(&lfs, "c") => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// create our destination first, just in case
|
|
if (EXISTS) {
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, (INTERDIR) ? "a/batman" : "batman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "hmmmmmmmm", strlen("hmmmmmmmm"))
|
|
=> strlen("hmmmmmmmm");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// create a zombie
|
|
lfsr_file_t zombie;
|
|
lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
if (!POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
lfsr_file_sync(&lfs, &zombie) => 0;
|
|
lfsr_remove(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
|
|
|
|
// create a file on top of the zombie
|
|
if (DIR) {
|
|
lfsr_mkdir(&lfs, (INTERDIR) ? "c/datman" : "datman") => 0;
|
|
} else {
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, (INTERDIR) ? "c/datman" : "datman",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, "catman!", strlen("catman!"))
|
|
=> strlen("catman!");
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
if (CLOSE == 2) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 2) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// rename while open
|
|
lfsr_rename(&lfs,
|
|
(INTERDIR) ? "c/datman" : "datman",
|
|
(INTERDIR) ? "a/batman" : "batman") => 0;
|
|
|
|
if (CLOSE <= 1 && REMOUNT <= 1) {
|
|
if (POSTHUMOUS) {
|
|
lfsr_file_write(&lfs, &zombie,
|
|
"WoOoOoOoOoO", strlen("WoOoOoOoOoO"))
|
|
=> strlen("WoOoOoOoOoO");
|
|
}
|
|
|
|
// we should still be able to read our file
|
|
lfsr_file_rewind(&lfs, &zombie) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &zombie, rbuf, sizeof(rbuf))
|
|
=> strlen("WoOoOoOoOoO");
|
|
assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0);
|
|
|
|
if (CLOSE == 1) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
if (REMOUNT == 1) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
}
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfsr_fs_mkconsistent(&lfs) => 0;
|
|
}
|
|
|
|
// make sure the new file is readable
|
|
// via stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, (INTERDIR) ? "a/batman" : "batman", &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
lfsr_stat(&lfs, (INTERDIR) ? "c/datman" : "datman", &info) => LFS_ERR_NOENT;
|
|
// via readdir
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, (INTERDIR) ? "a" : "/") => 0;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "batman") == 0);
|
|
if (DIR) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == strlen("catman!"));
|
|
}
|
|
if (!INTERDIR) {
|
|
for (lfs_size_t i = 0; i < DISTANCE; i++) {
|
|
char name[256];
|
|
sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == 0);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
// via open
|
|
if (DIR) {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
} else {
|
|
lfsr_file_t file_;
|
|
lfsr_file_open(&lfs, &file_, (INTERDIR) ? "a/batman" : "batman",
|
|
LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("catman!");
|
|
assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0);
|
|
lfsr_file_close(&lfs, &file_) => 0;
|
|
}
|
|
|
|
if (CLOSE == 0) {
|
|
lfsr_file_close(&lfs, &zombie) => 0;
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# one particularly nasty case is renaming over an mdir split, since shrubs
|
|
# can be moved around quite a few times when that happens
|
|
#
|
|
# here we spam renames over an increasing number of files to hopefully hit
|
|
# that case
|
|
#
|
|
[cases.test_forphans_mv_split]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# keep unsynced data open?
|
|
defines.UNSYNC = [false, true]
|
|
# keep a desynced file open?
|
|
defines.DESYNC = [false, true]
|
|
# keep a zombie open?
|
|
defines.ZOMBIE = [false, true]
|
|
if = '(SIZE*N*(1+DESYNC+ZOMBIE))/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// we need a file handle for each file + desync + zombie
|
|
lfsr_file_t files[N];
|
|
lfsr_file_t desyncs[N];
|
|
lfsr_file_t zombies[N];
|
|
|
|
// create this many files while renaming
|
|
uint32_t prng = 42;
|
|
uint32_t unsync_prng = 43;
|
|
uint32_t desync_prng = 44;
|
|
uint32_t zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// create a zombie?
|
|
if (ZOMBIE) {
|
|
lfsr_file_open(&lfs, &zombies[i], "batman!!!",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &zombies[i], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &zombies[i]) => 0;
|
|
lfsr_remove(&lfs, "batman!!!") => 0;
|
|
}
|
|
|
|
// always create as first file
|
|
lfsr_file_open(&lfs, &files[i], "batman!!!",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &files[i]) => 0;
|
|
|
|
// keep unsynced data?
|
|
if (UNSYNC) {
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// keep a desynced file open?
|
|
if (DESYNC) {
|
|
lfsr_file_open(&lfs, &desyncs[i], "batman!!!",
|
|
LFS_O_RDWR | LFS_O_DESYNC | LFS_O_TRUNC) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &desyncs[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// rename!
|
|
char name[256];
|
|
sprintf(name, "batman%03x", i);
|
|
lfsr_rename(&lfs, "batman!!!", name) => 0;
|
|
}
|
|
|
|
// check that renames worked
|
|
prng = 42;
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman!!!", &info) => LFS_ERR_NOENT;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "batman%03x", i);
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that file handles are as expected
|
|
prng = (!UNSYNC) ? 42 : 43;
|
|
desync_prng = 44;
|
|
zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check size
|
|
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
|
|
// check any desynced files
|
|
if (DESYNC) {
|
|
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
|
|
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// check any zombied files
|
|
if (ZOMBIE) {
|
|
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
|
|
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
// try syncing any unsynced files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_sync(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
// check that sync worked
|
|
prng = (!UNSYNC) ? 42 : 43;
|
|
lfsr_stat(&lfs, "batman!!!", &info) => LFS_ERR_NOENT;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "batman%03x", i);
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that file handles are as expected
|
|
prng = (!UNSYNC) ? 42 : 43;
|
|
desync_prng = 44;
|
|
zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check size
|
|
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
|
|
// check any desynced files
|
|
if (DESYNC) {
|
|
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
|
|
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// check any zombied files
|
|
if (ZOMBIE) {
|
|
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
|
|
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
// try rewriting our open files
|
|
uint32_t rewrite_prng = 52;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
// check that rewrites worked
|
|
rewrite_prng = 52;
|
|
lfsr_stat(&lfs, "batman!!!", &info) => LFS_ERR_NOENT;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "batman%03x", i);
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that file handles are as expected
|
|
rewrite_prng = 52;
|
|
desync_prng = 44;
|
|
zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check size
|
|
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
|
|
// check any desynced files
|
|
if (DESYNC) {
|
|
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
|
|
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// check any zombied files
|
|
if (ZOMBIE) {
|
|
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
|
|
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
// cleanup files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
if (DESYNC) {
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &desyncs[i]) => 0;
|
|
}
|
|
}
|
|
if (ZOMBIE) {
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &zombies[i]) => 0;
|
|
}
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_forphans_mv_split_backwards]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
# keep unsynced data open?
|
|
defines.UNSYNC = [false, true]
|
|
# keep a desynced file open?
|
|
defines.DESYNC = [false, true]
|
|
# keep a zombie open?
|
|
defines.ZOMBIE = [false, true]
|
|
if = '(SIZE*N*(1+DESYNC+ZOMBIE))/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// we need a file handle for each file + desync + zombie
|
|
lfsr_file_t files[N];
|
|
lfsr_file_t desyncs[N];
|
|
lfsr_file_t zombies[N];
|
|
|
|
// create this many files while renaming
|
|
uint32_t prng = 42;
|
|
uint32_t unsync_prng = 43;
|
|
uint32_t desync_prng = 44;
|
|
uint32_t zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// create a zombie?
|
|
if (ZOMBIE) {
|
|
lfsr_file_open(&lfs, &zombies[i], "batman???",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &zombies[i], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &zombies[i]) => 0;
|
|
lfsr_remove(&lfs, "batman???") => 0;
|
|
}
|
|
|
|
// always create as last file
|
|
lfsr_file_open(&lfs, &files[i], "batman???",
|
|
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &files[i]) => 0;
|
|
|
|
// keep unsynced data?
|
|
if (UNSYNC) {
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// keep a desynced file open?
|
|
if (DESYNC) {
|
|
lfsr_file_open(&lfs, &desyncs[i], "batman???",
|
|
LFS_O_RDWR | LFS_O_DESYNC | LFS_O_TRUNC) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &desyncs[i], wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// rename!
|
|
char name[256];
|
|
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
|
|
lfsr_rename(&lfs, "batman???", name) => 0;
|
|
}
|
|
|
|
// check that renames worked
|
|
prng = 42;
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "batman???", &info) => LFS_ERR_NOENT;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that file handles are as expected
|
|
prng = (!UNSYNC) ? 42 : 43;
|
|
desync_prng = 44;
|
|
zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check size
|
|
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
|
|
// check any desynced files
|
|
if (DESYNC) {
|
|
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
|
|
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// check any zombied files
|
|
if (ZOMBIE) {
|
|
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
|
|
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
// try syncing any unsynced files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_sync(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
// check that sync worked
|
|
prng = (!UNSYNC) ? 42 : 43;
|
|
lfsr_stat(&lfs, "batman???", &info) => LFS_ERR_NOENT;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that file handles are as expected
|
|
prng = (!UNSYNC) ? 42 : 43;
|
|
desync_prng = 44;
|
|
zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check size
|
|
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
|
|
// check any desynced files
|
|
if (DESYNC) {
|
|
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
|
|
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// check any zombied files
|
|
if (ZOMBIE) {
|
|
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
|
|
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
// try rewriting our open files
|
|
uint32_t rewrite_prng = 52;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &files[i], wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &files[i]) => 0;
|
|
}
|
|
|
|
// check that rewrites worked
|
|
rewrite_prng = 52;
|
|
lfsr_stat(&lfs, "batman???", &info) => LFS_ERR_NOENT;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "batman%03x", (uint32_t)(N-1-i));
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that file handles are as expected
|
|
rewrite_prng = 52;
|
|
desync_prng = 44;
|
|
zombie_prng = 45;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check size
|
|
assert(lfsr_file_size(&lfs, &files[i]) == SIZE);
|
|
|
|
// try reading the file
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &files[i]) => 0;
|
|
lfsr_file_read(&lfs, &files[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
|
|
// check any desynced files
|
|
if (DESYNC) {
|
|
assert(lfsr_file_size(&lfs, &desyncs[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &desyncs[i]) => 0;
|
|
lfsr_file_read(&lfs, &desyncs[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// check any zombied files
|
|
if (ZOMBIE) {
|
|
assert(lfsr_file_size(&lfs, &zombies[i]) == SIZE);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_rewind(&lfs, &zombies[i]) => 0;
|
|
lfsr_file_read(&lfs, &zombies[i], rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
}
|
|
|
|
// cleanup files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &files[i]) => 0;
|
|
}
|
|
if (DESYNC) {
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &desyncs[i]) => 0;
|
|
}
|
|
}
|
|
if (ZOMBIE) {
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_file_close(&lfs, &zombies[i]) => 0;
|
|
}
|
|
}
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# fuzz tests involving many orphans + zombies, this gets a bit crazy
|
|
[cases.test_forphans_orphanzombie_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
# do more ops than files to encourage file rewrites
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 5;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz tests involving many orphans + zombies + dirs, this gets a bit crazy
|
|
[cases.test_forphans_orphanzombiedir_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
# do more ops than files to encourage file rewrites
|
|
defines.OPS = '2*N'
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_BUFFER_SIZE/2',
|
|
'2*FILE_BUFFER_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.SEED = 'range(20)'
|
|
fuzz = 'SEED'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isdirs = malloc(N*sizeof(bool));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs_size_t x;
|
|
bool orphan;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfsr_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < OPS; i++) {
|
|
nonsense:;
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 8;
|
|
|
|
// open a new file?
|
|
if (op == 0) {
|
|
if (sim_file_count >= N) {
|
|
goto nonsense;
|
|
}
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// already exists?
|
|
bool orphan = true;
|
|
uint32_t wprng = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
orphan = false;
|
|
wprng = sim_prngs[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (orphan) {
|
|
wprng = TEST_PRNG(&prng);
|
|
}
|
|
|
|
// open in our sim
|
|
lfs_size_t j = sim_file_count;
|
|
sim_files[j] = malloc(sizeof(sim_file_t));
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->orphan = orphan;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// open the actual file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_file_open(&lfs, &sim_files[j]->file, name,
|
|
LFS_O_RDWR | LFS_O_CREAT) => 0;
|
|
|
|
// write some initial data if we don't exist
|
|
if (orphan) {
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
lfs_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// insert into our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
sim_files[k]->orphan = false;
|
|
sim_files[k]->prng = wprng;
|
|
}
|
|
}
|
|
}
|
|
|
|
// write to the file
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_sync(&lfs, &sim_files[j]->file)
|
|
=> (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT;
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_file_count;
|
|
|
|
// this doesn't really test anything, but if we don't close
|
|
// files eventually everything will end up zombies
|
|
|
|
// close the file without affected disk
|
|
lfsr_file_desync(&lfs, &sim_files[j]->file) => 0;
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// rename a file?
|
|
} else if (op == 4) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool isdir = sim_isdirs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// type mismatch?
|
|
if (sim_isdirs[k] != isdir) {
|
|
goto nonsense;
|
|
}
|
|
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[j], &sim_isdirs[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isdirs[k] = isdir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
// move source files
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->x = y;
|
|
|
|
// mark target files as zombied
|
|
} else if (sim_files[k]->x == y) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "batman%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "batman%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
|
|
// toss a directory into the mix
|
|
} else if (op == 5) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// insert into our sim, use negative numbers for dirs
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// already seen?
|
|
if (k < sim_size && sim[k] == x) {
|
|
goto nonsense;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = 0;
|
|
sim_isdirs[k] = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x) {
|
|
sim_files[k]->zombie = true;
|
|
}
|
|
}
|
|
|
|
// make the directory
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
lfsr_mkdir(&lfs, name) => 0;
|
|
}
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
} else {
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
if (sim_isdirs[j]) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
|
|
|
} else {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
for (lfs_size_t j = 0; j < sim_file_count; j++) {
|
|
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|