Files
littlefs/tests/test_grow.toml
T
Christopher Haster 18190054d9 Trying to better use uncreat/zombie/orphan terms in tests
Renamed a bunch of tests:

- test_forphans_create_* -> test_forphans_uncreat_*
- test_forphans_cleanup_opened -> test_forphans_cleanup_open
- test_forphans_cleanup_orphaned -> test_forphans_cleanup_uncreat
- test_forphans_orphanzombie_fuzz -> test_forphans_uz_fuzz
- test_forphans_orphanzombiedir_fuzz -> test_forphans_uzd_fuzz
- test_*_oz_fuzz -> test_*_uz_fuzz
- test_*_ozd_fuzz -> test_*_uzd_fuzz
- test_traversal_*_orphan_* -> test_traversal_*_uncreat_*
- test_traversal_*_orphaned -> test_traversal_*_uncreat
- test_attrs_fattr_orphan -> test_attrs_fattr_uncreat

And renamed a number of variables and things.
2025-01-28 14:41:45 -06:00

2964 lines
99 KiB
TOML

# Test variable block counts and grow related things
after = [
'test_dirs',
'test_files',
'test_fwrite',
'test_forphans',
'test_alloc',
'test_mount',
]
# test we can mount a filesystem with fewer blocks
[cases.test_grow_mount_smaller]
defines.SMALLER_BLOCK_COUNT = [
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'2',
]
defines.BIGGER_BLOCK_COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
]
if = 'BIGGER_BLOCK_COUNT > SMALLER_BLOCK_COUNT'
code = '''
// create a smaller fs
struct lfs_config cfg = *CFG;
cfg.block_count = SMALLER_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// do some work
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[1024] = "Hello World!";
lfs_size_t wsize = strlen((char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
uint8_t rbuf[1024];
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
//////
// try to mount with a bigger block count
cfg = *CFG;
cfg.block_count = BIGGER_BLOCK_COUNT;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// file still exists?
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
// do some work
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
strcpy((char*)wbuf, "Chris was here!");
wsize = strlen((char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
// stays after a mount?
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// file still exists?
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
'''
# test we _can't_ mount a filesystem with more blocks
[cases.test_grow_mount_bigger]
defines.SMALLER_BLOCK_COUNT = [
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'2',
]
defines.BIGGER_BLOCK_COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
]
if = 'BIGGER_BLOCK_COUNT > SMALLER_BLOCK_COUNT'
code = '''
// create a bigger fs
struct lfs_config cfg = *CFG;
cfg.block_count = BIGGER_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BIGGER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// do some work
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[1024] = "Hello World!";
lfs_size_t wsize = strlen((char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
uint8_t rbuf[1024];
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
//////
// try to mount with a smaller block count
cfg = *CFG;
cfg.block_count = SMALLER_BLOCK_COUNT;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => LFS_ERR_NOTSUP;
'''
# test we can grow a filesystem
[cases.test_grow_grow]
defines.SMALLER_BLOCK_COUNT = [
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'2',
]
defines.BIGGER_BLOCK_COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
]
if = 'BIGGER_BLOCK_COUNT > SMALLER_BLOCK_COUNT'
code = '''
// create a smaller fs
struct lfs_config cfg = *CFG;
cfg.block_count = SMALLER_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// do some work
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[1024] = "Hello World!";
lfs_size_t wsize = strlen((char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
uint8_t rbuf[1024];
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
//////
// try to grow our filesystem
cfg = *CFG;
cfg.block_count = BIGGER_BLOCK_COUNT;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
lfsr_fs_grow(&lfs, BIGGER_BLOCK_COUNT) => 0;
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BIGGER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// file still exists?
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
// do some work
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
strcpy((char*)wbuf, "Chris was here!");
wsize = strlen((char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
// stays after a mount?
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BIGGER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// file still exists?
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
'''
# growing to the same size should do nothing
[cases.test_grow_noop]
defines.SMALLER_BLOCK_COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'2',
]
code = '''
// create a smaller fs
struct lfs_config cfg = *CFG;
cfg.block_count = SMALLER_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// do some work
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[1024] = "Hello World!";
lfs_size_t wsize = strlen((char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
uint8_t rbuf[1024];
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
//////
// try to grow to same size
cfg = *CFG;
cfg.block_count = SMALLER_BLOCK_COUNT;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
lfsr_fs_grow(&lfs, SMALLER_BLOCK_COUNT) => 0;
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// file still exists?
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
// do some work
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
strcpy((char*)wbuf, "Chris was here!");
wsize = strlen((char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
// stays after a mount?
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// file still exists?
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfsr_file_read(&lfs, &file, rbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_unmount(&lfs) => 0;
'''
# These tests try various fuzz tests while incrementally growing the
# filesystem. When encountering LFS_ERR_NOSPC, the filesystem is grown by
# one block. Hopefully this will catch most grow-related bugs.
#
[cases.test_grow_incr_spam_dir_many]
defines.INIT_BLOCK_COUNT = 2
defines.REMOUNT = [false, true]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
code = '''
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
again:;
char name[256];
sprintf(name, "dir%03x", i);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// grm should be zero here
assert(lfs.grm_p[0] == 0);
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_open(&lfs, &dir, name) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &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_grow_incr_spam_dir_fuzz]
defines.INIT_BLOCK_COUNT = 2
defines.REMOUNT = [false, true]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
defines.OPS = 1024
defines.SEED = 'range(10)'
fuzz = 'SEED'
code = '''
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
lfs_size_t sim_size = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
again:;
uint32_t prng_ = prng;
// choose a pseudo-random op, either mkdir, remove, or rename
uint8_t op = TEST_PRNG(&prng_) % 3;
if (op == 0 || sim_size == 0) {
// choose a pseudo-random number, truncate to 3 hexadecimals
lfs_size_t x = TEST_PRNG(&prng_) % N;
// create a directory here
char name[256];
sprintf(name, "dir%03x", x);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// insert into our sim
for (lfs_size_t j = 0;; j++) {
if (j >= sim_size || sim[j] >= x) {
// already seen?
if (j < sim_size && sim[j] == x) {
// do nothing
} else {
// insert
memmove(&sim[j+1], &sim[j],
(sim_size-j)*sizeof(lfs_size_t));
sim_size += 1;
sim[j] = x;
}
break;
}
}
} else if (op == 1) {
// choose a pseudo-random entry to delete
lfs_size_t j = TEST_PRNG(&prng_) % sim_size;
lfs_size_t x = sim[j];
// remove this directory
char name[256];
sprintf(name, "dir%03x", x);
int err = lfsr_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// delete from our sim
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
sim_size -= 1;
} else {
// choose a pseudo-random entry to rename, and a pseudo-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;
// rename this directory
char old_name[256];
sprintf(old_name, "dir%03x", x);
char new_name[256];
sprintf(new_name, "dir%03x", y);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// already seen and not a noop?
if (k < sim_size && sim[k] == y && x != y) {
// just delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
sim_size -= 1;
} else {
// first delete
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
sim[k] = y;
}
break;
}
}
}
prng = prng_;
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
int err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// grm should be zero here
assert(lfs.grm_p[0] == 0);
// test that our directories match our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "dir%03x", sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%03x", sim[j]);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
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, "dir%03x", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
}
// clean up sim/lfs
free(sim);
lfsr_unmount(&lfs) => 0;
'''
[cases.test_grow_incr_spam_file_many]
defines.INIT_BLOCK_COUNT = 2
defines.REMOUNT = [false, true]
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',
]
if = '(SIZE*N)/BLOCK_SIZE <= 32'
code = '''
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
again:;
uint32_t prng_ = prng;
// create this many files
char name[256];
sprintf(name, "amethyst%03x", i);
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfsr_file_t file;
int err = lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
lfs_ssize_t d = lfsr_file_write(&lfs, &file, wbuf, SIZE);
assert(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
lfsr_file_close(&lfs, &file) => 0;
goto grow;
}
err = lfsr_file_close(&lfs, &file);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
prng = prng_;
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// check that our writes worked
prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "amethyst%03x", i);
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);
// try reading the file, note we reset prng above
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;
}
}
lfsr_unmount(&lfs) => 0;
'''
[cases.test_grow_incr_spam_file_fuzz]
defines.INIT_BLOCK_COUNT = 2
defines.REMOUNT = [false, true]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.OPS = 1024
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(10)'
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
code = '''
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// 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;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
again:;
uint32_t prng_ = prng;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng_) % 3;
// creating a new file?
if (op == 0 || sim_size == 0) {
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng_) % N;
// associate each file with a prng that generates its contents
uint32_t wprng = TEST_PRNG(&prng_);
// create a file here
char name[256];
sprintf(name, "amethyst%03x", x);
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfsr_file_t file;
int err = lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
lfs_ssize_t d = lfsr_file_write(&lfs, &file, wbuf, SIZE);
assert(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
lfsr_file_close(&lfs, &file) => 0;
goto grow;
}
err = lfsr_file_close(&lfs, &file);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// insert into our sim
for (lfs_size_t j = 0;; j++) {
if (j >= sim_size || sim[j] >= x) {
// already seen?
if (j < sim_size && sim[j] == x) {
// new prng
sim_prngs[j] = wprng;
} else {
// insert
memmove(&sim[j+1], &sim[j],
(sim_size-j)*sizeof(lfs_size_t));
memmove(&sim_prngs[j+1], &sim_prngs[j],
(sim_size-j)*sizeof(uint32_t));
sim_size += 1;
sim[j] = x;
sim_prngs[j] = wprng;
}
break;
}
}
// deleting a file?
} else if (op == 1) {
// choose a random file to delete
lfs_size_t j = TEST_PRNG(&prng_) % sim_size;
lfs_size_t x = sim[j];
// delete this file
char name[256];
sprintf(name, "amethyst%03x", x);
int err = lfsr_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// 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;
// renaming a file?
} else {
// 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];
// rename this file
char old_name[256];
sprintf(old_name, "amethyst%03x", x);
char new_name[256];
sprintf(new_name, "amethyst%03x", y);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// 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;
}
}
}
prng = prng_;
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
int err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// check that our files match our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "amethyst%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, "amethyst%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;
// check the file contents
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "amethyst%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;
}
}
// clean up sim/lfs
free(sim);
free(sim_prngs);
lfsr_unmount(&lfs) => 0;
'''
[cases.test_grow_incr_spam_uz_fuzz]
defines.INIT_BLOCK_COUNT = 2
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.OPS = 1024
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(10)'
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
code = '''
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
lfs_size_t sim_size = 0;
typedef struct sim_file {
lfs_size_t x;
bool uncreat;
bool zombie;
uint32_t prng;
lfsr_file_t file;
} sim_file_t;
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
lfs_size_t sim_file_count = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
again:;
uint32_t prng_ = prng;
nonsense:;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng_) % 5;
// open a new file?
if (op == 0) {
if (sim_file_count >= N) {
goto nonsense;
}
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng_) % N;
// already exists?
bool uncreat = true;
uint32_t wprng = 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
if (sim[j] == x) {
uncreat = false;
wprng = sim_prngs[j];
break;
}
}
// choose a random seed if we don't exist
if (uncreat) {
wprng = TEST_PRNG(&prng_);
}
lfs_size_t j = sim_file_count;
sim_files[j] = malloc(sizeof(sim_file_t));
// open the actual file
char name[256];
sprintf(name, "batman%03x", x);
int err = lfsr_file_open(&lfs, &sim_files[j]->file, name,
LFS_O_RDWR | LFS_O_CREAT);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
free(sim_files[j]);
goto grow;
}
// write some initial data if we don't exist
if (uncreat) {
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file,
wbuf, SIZE);
LFS_ASSERT(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
free(sim_files[j]);
goto grow;
}
}
// open in our sim
sim_files[j]->x = x;
sim_files[j]->uncreat = uncreat;
sim_files[j]->zombie = false;
sim_files[j]->prng = wprng;
sim_file_count++;
// write/rewrite a file?
} else if (op == 1) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs_size_t j = TEST_PRNG(&prng_) % sim_file_count;
lfs_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng_);
// write to the file
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file,
wbuf, SIZE);
assert(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
goto grow;
}
int err = lfsr_file_sync(&lfs, &sim_files[j]->file);
assert(err == ((!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT)
|| err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// insert into our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = wprng;
}
break;
}
}
// update related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
sim_files[k]->uncreat = false;
sim_files[k]->prng = wprng;
}
}
}
// 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 this file
char name[256];
sprintf(name, "batman%03x", x);
int err = lfsr_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// 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;
}
}
// 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];
// rename this file
char old_name[256];
sprintf(old_name, "batman%03x", x);
char new_name[256];
sprintf(new_name, "batman%03x", y);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// 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;
}
}
}
prng = prng_;
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
int err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
// 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_desync(&lfs, &sim_files[j]->file) => 0;
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfsr_unmount(&lfs) => 0;
'''
[cases.test_grow_incr_spam_uzd_fuzz]
defines.INIT_BLOCK_COUNT = 2
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.OPS = 1024
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(10)'
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
code = '''
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
bool *sim_isdirs = malloc(N*sizeof(bool));
lfs_size_t sim_size = 0;
typedef struct sim_file {
lfs_size_t x;
bool uncreat;
bool zombie;
uint32_t prng;
lfsr_file_t file;
} sim_file_t;
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
lfs_size_t sim_file_count = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
again:;
uint32_t prng_ = prng;
nonsense:;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng_) % 8;
// open a new file?
if (op == 0) {
if (sim_file_count >= N) {
goto nonsense;
}
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng_) % N;
// already exists?
bool uncreat = true;
uint32_t wprng = 0;
for (lfs_size_t j = 0; j < sim_size; j++) {
if (sim[j] == x) {
if (sim_isdirs[j]) {
goto nonsense;
}
uncreat = false;
wprng = sim_prngs[j];
break;
}
}
// choose a random seed if we don't exist
if (uncreat) {
wprng = TEST_PRNG(&prng_);
}
lfs_size_t j = sim_file_count;
sim_files[j] = malloc(sizeof(sim_file_t));
// open the actual file
char name[256];
sprintf(name, "batman%03x", x);
int err = lfsr_file_open(&lfs, &sim_files[j]->file, name,
LFS_O_RDWR | LFS_O_CREAT);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
free(sim_files[j]);
goto grow;
}
// write some initial data if we don't exist
if (uncreat) {
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file,
wbuf, SIZE);
assert(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
free(sim_files[j]);
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
goto grow;
}
}
// open in our sim
sim_files[j]->x = x;
sim_files[j]->uncreat = uncreat;
sim_files[j]->zombie = false;
sim_files[j]->prng = wprng;
sim_file_count++;
// write/rewrite a file?
} else if (op == 1) {
if (sim_file_count == 0) {
goto nonsense;
}
// choose a random file handle
lfs_size_t j = TEST_PRNG(&prng_) % sim_file_count;
lfs_size_t x = sim_files[j]->x;
// choose a random seed
uint32_t wprng = TEST_PRNG(&prng_);
// write to the file
lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0;
uint8_t wbuf[SIZE];
uint32_t wprng_ = wprng;
for (lfs_size_t k = 0; k < SIZE; k++) {
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file,
wbuf, SIZE);
assert(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
goto grow;
}
int err = lfsr_file_sync(&lfs, &sim_files[j]->file);
assert(err == ((!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT)
|| err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// update sim
sim_files[j]->prng = wprng;
if (!sim_files[j]->zombie) {
// insert into our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
// new prng
sim_prngs[k] = wprng;
} else {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
(sim_size-k)*sizeof(bool));
sim_size += 1;
sim[k] = x;
sim_prngs[k] = wprng;
sim_isdirs[k] = false;
}
break;
}
}
// update related sim files
for (lfs_size_t k = 0; k < sim_file_count; k++) {
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
sim_files[k]->uncreat = false;
sim_files[k]->prng = wprng;
}
}
}
// 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 this file
char name[256];
sprintf(name, "batman%03x", x);
int err = lfsr_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// 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;
}
}
// 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];
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;
}
}
break;
}
}
// rename this file
char old_name[256];
sprintf(old_name, "batman%03x", x);
char new_name[256];
sprintf(new_name, "batman%03x", y);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// update our sim
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// renaming and replacing
if (k < sim_size && sim[k] == y && x != y) {
// delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
memmove(&sim_prngs[j], &sim_prngs[j+1],
(sim_size-(j+1))*sizeof(uint32_t));
memmove(&sim_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;
}
}
// toss a directory into the mix
} else if (op == 5) {
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng_) % N;
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// already seen?
if (k < sim_size && sim[k] == x) {
goto nonsense;
}
break;
}
}
// make the directory
char name[256];
sprintf(name, "batman%03x", x);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// insert into our sim, use negative numbers for dirs
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= x) {
// insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
memmove(&sim_prngs[k+1], &sim_prngs[k],
(sim_size-k)*sizeof(uint32_t));
memmove(&sim_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;
}
}
}
prng = prng_;
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
int err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
// 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_desync(&lfs, &sim_files[j]->file) => 0;
lfsr_file_close(&lfs, &sim_files[j]->file) => 0;
free(sim_files[j]);
}
free(sim_files);
lfsr_unmount(&lfs) => 0;
'''
# A general purpose powerloss fuzz test
#
#
# Under powerloss, we can't really keep track of a sim reliably/
# efficiently, instead just do random operations, store a counter in a
# special file so we know how much progress has been made, and hope for
# the best. Most likely an internal assert will trigger if anything goes
# wrong.
#
[cases.test_grow_incr_spam_f_pl_fuzz]
defines.INIT_BLOCK_COUNT = 2
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.OPS = 256
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(10)'
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
}
// keep some test state on disk to survive powerloss
typedef struct fuzz_state {
lfs_size_t i;
uint32_t prng;
} fuzz_state_t;
fuzz_state_t state = {.i = 0, .prng = SEED};
lfsr_file_t state_file;
err = lfsr_file_open(&lfs, &state_file, "state", LFS_O_RDONLY);
assert(!err || err == LFS_ERR_NOENT);
if (!err) {
lfsr_file_read(&lfs, &state_file,
&state, sizeof(state)) => sizeof(state);
lfsr_file_close(&lfs, &state_file) => 0;
}
uint32_t prng = state.prng;
for (lfs_size_t i = state.i; i < OPS; i++) {
again:;
uint32_t prng_ = prng;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng_) % 3;
// keep test files in a separate directory
err = lfsr_mkdir(&lfs, "test");
assert(!err || err == LFS_ERR_EXIST || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// how many files do we have?
lfs_size_t count = 0;
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "test") => 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);
while (true) {
err = lfsr_dir_read(&lfs, &dir, &info);
assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
break;
}
assert(strlen(info.name) == strlen("amethyst..."));
assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
count++;
}
lfsr_dir_close(&lfs, &dir) => 0;
// creating a new file?
if (op == 0 || count == 0) {
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng_) % N;
uint32_t wprng = TEST_PRNG(&prng_);
// create a file here
char name[256];
sprintf(name, "test/amethyst%03x", x);
uint8_t wbuf[SIZE];
uint8_t ck = 0;
for (lfs_size_t j = 0; j < SIZE-1; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
ck = (ck + (wbuf[j] - 'a')) % 26;
}
// make the sum equal to 'a' mod 26
if (SIZE > 0) {
wbuf[SIZE-1] = 'a' + ((26 - ck) % 26);
}
lfsr_file_t file;
err = lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
lfs_ssize_t d = lfsr_file_write(&lfs, &file, wbuf, SIZE);
assert(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
lfsr_file_close(&lfs, &file) => 0;
goto grow;
}
err = lfsr_file_close(&lfs, &file);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// deleting a file?
} else if (op == 1) {
// choose a random file to delete
lfs_size_t j = TEST_PRNG(&prng_) % count;
// find the file
lfsr_dir_open(&lfs, &dir, "test") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
for (lfs_size_t k = 0; k <= j; k++) {
lfsr_dir_read(&lfs, &dir, &info) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
// delete this file
char name[256];
assert(strlen(info.name) == strlen("amethyst..."));
sprintf(name, "test/%s", info.name);
err = lfsr_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// renaming a file?
} else {
// choose a random file to rename, and a random number to
// rename to
lfs_size_t j = TEST_PRNG(&prng_) % count;
lfs_size_t y = TEST_PRNG(&prng_) % N;
// find the file
lfsr_dir_open(&lfs, &dir, "test") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
for (lfs_size_t k = 0; k <= j; k++) {
lfsr_dir_read(&lfs, &dir, &info) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
// rename this file
char old_name[256];
assert(strlen(info.name) == strlen("amethyst..."));
sprintf(old_name, "test/%s", info.name);
char new_name[256];
sprintf(new_name, "test/amethyst%03x", y);
err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
}
// update our state file
state.i = i;
state.prng = prng_;
err = lfsr_file_open(&lfs, &state_file, "state",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
lfs_ssize_t d = lfsr_file_write(&lfs, &state_file,
&state, sizeof(state));
assert(d == sizeof(state) || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
lfsr_file_close(&lfs, &state_file) => 0;
goto grow;
}
err = lfsr_file_close(&lfs, &state_file);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
prng = prng_;
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// check that things look more-or-less ok
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "test") => 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);
while (true) {
int err = lfsr_dir_read(&lfs, &dir, &info);
assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
break;
}
assert(strlen(info.name) == strlen("amethyst..."));
assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// at least try to read the files
char name[256];
sprintf(name, "test/%s", info.name);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
// all data should be lowercase ascii
for (lfs_size_t j = 0; j < SIZE; j++) {
assert(rbuf[j] >= 'a' && rbuf[j] <= 'z');
}
// sum should be equal to 'a' mod 26
uint8_t ck = 0;
for (lfs_size_t j = 0; j < SIZE; j++) {
ck = (ck + (rbuf[j] - 'a')) % 26;
}
assert(ck == 0);
lfsr_file_close(&lfs, &file) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# A general purpose powerloss fuzz test, with directories!
#
# Under powerloss, we can't really keep track of a sim reliably/
# efficiently, instead just do random operations, store a counter in a
# special file so we know how much progress has been made, and hope for
# the best. Most likely an internal assert will trigger if anything goes
# wrong.
#
[cases.test_grow_incr_spam_fd_pl_fuzz]
defines.INIT_BLOCK_COUNT = 2
# note dirs x files grows O(n^2)
defines.N = [1, 2, 4, 8]
defines.M = 'N'
defines.OPS = 256
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(10)'
fuzz = 'SEED'
if = '(SIZE*N)/BLOCK_SIZE <= 16'
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
// start with a small number of blocks
struct lfs_config cfg = *CFG;
cfg.block_count = INIT_BLOCK_COUNT;
lfsr_format(&lfs, LFS_F_RDWR, &cfg) => 0;
// mount with maximum block count
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// fsstat up to date?
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == INIT_BLOCK_COUNT);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
}
// keep some test state on disk to survive powerloss
typedef struct fuzz_state {
lfs_size_t i;
uint32_t prng;
} fuzz_state_t;
fuzz_state_t state = {.i = 0, .prng = SEED};
lfsr_file_t state_file;
err = lfsr_file_open(&lfs, &state_file, "state", LFS_O_RDONLY);
assert(!err || err == LFS_ERR_NOENT);
if (!err) {
lfsr_file_read(&lfs, &state_file,
&state, sizeof(state)) => sizeof(state);
lfsr_file_close(&lfs, &state_file) => 0;
}
uint32_t prng = state.prng;
for (lfs_size_t i = state.i; i < OPS; i++) {
again:;
uint32_t prng_ = prng;
// choose which operation to do
uint8_t op = TEST_PRNG(&prng_) % 6;
// keep test files in a separate directory
err = lfsr_mkdir(&lfs, "test");
assert(!err || err == LFS_ERR_EXIST || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// how many dirs do we have?
lfs_size_t dir_count = 0;
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "test") => 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);
while (true) {
err = lfsr_dir_read(&lfs, &dir, &info);
assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
break;
}
assert(strlen(info.name) == strlen("quartz..."));
assert(memcmp(info.name, "quartz", strlen("quartz")) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
dir_count++;
}
lfsr_dir_close(&lfs, &dir) => 0;
// dir op?
if (op < 3 || dir_count == 0) {
// creating a new dir?
if (op == 0 || dir_count == 0) {
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng_) % N;
// create a dir here
char name[256];
sprintf(name, "test/quartz%03x", x);
err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// deleting a dir?
} else if (op == 1) {
// choose a random dir to delete
lfs_size_t j = TEST_PRNG(&prng_) % dir_count;
// find the dir
lfsr_dir_open(&lfs, &dir, "test") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
for (lfs_size_t k = 0; k <= j; k++) {
lfsr_dir_read(&lfs, &dir, &info) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
// try to delete this dir, ignore non-empty dirs!
char name[256];
assert(strlen(info.name) == strlen("quartz..."));
sprintf(name, "test/%s", info.name);
err = lfsr_remove(&lfs, name);
assert(!err
|| err == LFS_ERR_NOTEMPTY
|| err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// renaming a dir?
} else {
// choose a random dir to rename, and a random number to
// rename to
lfs_size_t j = TEST_PRNG(&prng_) % dir_count;
lfs_size_t y = TEST_PRNG(&prng_) % N;
// find the dir
lfsr_dir_open(&lfs, &dir, "test") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
for (lfs_size_t k = 0; k <= j; k++) {
lfsr_dir_read(&lfs, &dir, &info) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
// rename this dir, ignore conflicts!
char old_name[256];
assert(strlen(info.name) == strlen("quartz..."));
sprintf(old_name, "test/%s", info.name);
char new_name[256];
sprintf(new_name, "test/quartz%03x", y);
err = lfsr_rename(&lfs, old_name, new_name);
assert(!err
|| err == LFS_ERR_NOTEMPTY
|| err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
}
// file op?
} else {
// choose a pseudo-random dir
lfs_size_t dir_i = TEST_PRNG(&prng_) % dir_count;
// find the dir
lfsr_dir_open(&lfs, &dir, "test") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
for (lfs_size_t k = 0; k <= dir_i; k++) {
lfsr_dir_read(&lfs, &dir, &info) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
char dir_path[256];
sprintf(dir_path, "test/%s", info.name);
// how many files do we have?
lfs_size_t count = 0;
lfsr_dir_open(&lfs, &dir, dir_path) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
while (true) {
err = lfsr_dir_read(&lfs, &dir, &info);
assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
break;
}
assert(strlen(info.name) == strlen("amethyst..."));
assert(memcmp(
info.name,
"amethyst", strlen("amethyst")) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
count++;
}
lfsr_dir_close(&lfs, &dir) => 0;
// creating a new file?
if (op == 3 || count == 0) {
// choose a pseudo-random number
lfs_size_t x = TEST_PRNG(&prng_) % M;
uint32_t wprng = TEST_PRNG(&prng_);
// create a file here
char name[256];
sprintf(name, "%s/amethyst%03x", dir_path, x);
uint8_t wbuf[SIZE];
uint8_t ck = 0;
for (lfs_size_t j = 0; j < SIZE-1; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
ck = (ck + (wbuf[j] - 'a')) % 26;
}
// make the sum equal to 'a' mod 26
if (SIZE > 0) {
wbuf[SIZE-1] = 'a' + ((26 - ck) % 26);
}
lfsr_file_t file;
err = lfsr_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
lfs_ssize_t d = lfsr_file_write(&lfs, &file, wbuf, SIZE);
assert(d == SIZE || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
lfsr_file_close(&lfs, &file) => 0;
goto grow;
}
err = lfsr_file_close(&lfs, &file);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// deleting a file?
} else if (op == 4) {
// choose a random file to delete
lfs_size_t j = TEST_PRNG(&prng_) % count;
// find the file
lfsr_dir_open(&lfs, &dir, dir_path) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
for (lfs_size_t k = 0; k <= j; k++) {
lfsr_dir_read(&lfs, &dir, &info) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
// delete this file
char name[256];
assert(strlen(info.name) == strlen("amethyst..."));
sprintf(name, "%s/%s", dir_path, info.name);
err = lfsr_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
// renaming a file?
} else {
// choose a random file to rename
lfs_size_t j = TEST_PRNG(&prng_) % count;
// find the file
lfsr_dir_open(&lfs, &dir, dir_path) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
for (lfs_size_t k = 0; k <= j; k++) {
lfsr_dir_read(&lfs, &dir, &info) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
// choose a random dir to rename to
lfs_size_t dir_j = TEST_PRNG(&prng_) % dir_count;
// find the dir
struct lfs_info info_;
lfsr_dir_open(&lfs, &dir, "test") => 0;
lfsr_dir_read(&lfs, &dir, &info_) => 0;
lfsr_dir_read(&lfs, &dir, &info_) => 0;
for (lfs_size_t k = 0; k <= dir_j; k++) {
lfsr_dir_read(&lfs, &dir, &info_) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
// choose a random file to rename to
lfs_size_t y = TEST_PRNG(&prng_) % M;
// rename this file
char old_name[256];
assert(strlen(info.name) == strlen("amethyst..."));
sprintf(old_name, "%s/%s", dir_path, info.name);
char new_name[256];
sprintf(new_name, "test/%s/amethyst%03x", info_.name, y);
err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
}
}
// update our state file
state.i = i;
state.prng = prng_;
err = lfsr_file_open(&lfs, &state_file, "state",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
lfs_ssize_t d = lfsr_file_write(&lfs, &state_file,
&state, sizeof(state));
assert(d == sizeof(state) || d == LFS_ERR_NOSPC);
if (d == LFS_ERR_NOSPC) {
lfsr_file_close(&lfs, &state_file) => 0;
goto grow;
}
err = lfsr_file_close(&lfs, &state_file);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
goto grow;
}
prng = prng_;
continue;
grow:;
// try growing the filesystem
struct lfs_fsinfo fsinfo;
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_count >= INIT_BLOCK_COUNT);
assert(fsinfo.block_count <= BLOCK_COUNT);
lfs_ssize_t used = lfsr_fs_size(&lfs);
assert(used >= 0);
// we may need to grow multiple blocks before the system gets unstuck
lfs_size_t block_count_ = fsinfo.block_count;
while (true) {
assert(block_count_ < BLOCK_COUNT);
block_count_ += 1;
err = lfsr_fs_grow(&lfs, block_count_);
assert(!err || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
continue;
}
break;
}
printf("grew %d/%d -> %d/%d\n",
used, fsinfo.block_count,
used, block_count_);
// fsstat up to date?
lfsr_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == block_count_);
assert(fsinfo.name_limit == LFS_NAME_MAX);
assert(fsinfo.file_limit == LFS_FILE_MAX);
goto again;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// check that things look more-or-less ok
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "test") => 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);
while (true) {
int err = lfsr_dir_read(&lfs, &dir, &info);
assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
break;
}
assert(strlen(info.name) == strlen("quartz..."));
assert(memcmp(info.name, "quartz", strlen("quartz")) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// check that our dirs look more-or-less ok
char name[256];
sprintf(name, "test/%s", info.name);
lfsr_dir_t dir_;
lfsr_dir_open(&lfs, &dir_, name) => 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);
while (true) {
err = lfsr_dir_read(&lfs, &dir_, &info_);
assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
break;
}
assert(strlen(info_.name) == strlen("amethyst..."));
assert(memcmp(
info_.name,
"amethyst", strlen("amethyst")) == 0);
assert(info_.type == LFS_TYPE_REG);
assert(info_.size == SIZE);
// at least try to read the files
sprintf(name, "test/%s/%s", info.name, info_.name);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
// all data should be lowercase ascii
for (lfs_size_t j = 0; j < SIZE; j++) {
assert(rbuf[j] >= 'a' && rbuf[j] <= 'z');
}
// sum should be equal to 'a' mod 26
uint8_t ck = 0;
for (lfs_size_t j = 0; j < SIZE; j++) {
ck = (ck + (rbuf[j] - 'a')) % 26;
}
assert(ck == 0);
lfsr_file_close(&lfs, &file) => 0;
}
lfsr_dir_close(&lfs, &dir_) => 0;
}
lfsr_dir_close(&lfs, &dir) => 0;
}
lfsr_unmount(&lfs) => 0;
'''