9e45249b29
In lfs3_fs_grow, we need to update any gbmaps to match the new disk
size. The actual patch to the gbmap is easy, but it does get a bit
delicate since we need to feed the gbmap with an allocator in the new
disk size.
Fortunately, the opportunistism of the gbmap allocator avoids any
catch-22 issues, as long as we make sure to not trigger any gbmap
rebuilds.
Adds a bit of code, but not much:
code stack ctx
before: 37168 2352 684
after: 37168 (+0.0%) 2352 (+0.0%) 684 (+0.0%)
code stack ctx
gbmap before: 39000 2456 800
gbmap after: 39116 (+0.3%) 2456 (+0.0%) 800 (+0.0%)
3158 lines
108 KiB
TOML
3158 lines
108 KiB
TOML
# Test variable block counts and grow related things
|
|
after = [
|
|
'test_dirs',
|
|
'test_files',
|
|
'test_fwrite',
|
|
'test_stickynotes',
|
|
'test_alloc',
|
|
'test_mount',
|
|
]
|
|
|
|
# Test both with and without the gbmap if available
|
|
defines.GBMAP = [false, true]
|
|
if = '''
|
|
LFS3_IFDEF_YES_GBMAP(
|
|
GBMAP,
|
|
LFS3_IFDEF_GBMAP(true, !GBMAP))
|
|
'''
|
|
defines.FORMAT_BLOCK_COUNT = '(GBMAP) ? 3 : 2'
|
|
|
|
|
|
# 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',
|
|
'FORMAT_BLOCK_COUNT',
|
|
]
|
|
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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = SMALLER_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// do some work
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[1024] = "Hello World!";
|
|
lfs3_size_t wsize = strlen((char*)wbuf);
|
|
lfs3_file_write(&lfs3, &file, wbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
uint8_t rbuf[1024];
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
//////
|
|
|
|
// try to mount with a bigger block count
|
|
cfg = *CFG;
|
|
cfg.block_count = BIGGER_BLOCK_COUNT;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// file still exists?
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
// do some work
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
|
|
strcpy((char*)wbuf, "Chris was here!");
|
|
wsize = strlen((char*)wbuf);
|
|
lfs3_file_write(&lfs3, &file, wbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// stays after a mount?
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// file still exists?
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 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',
|
|
'FORMAT_BLOCK_COUNT',
|
|
]
|
|
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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = BIGGER_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BIGGER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// do some work
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[1024] = "Hello World!";
|
|
lfs3_size_t wsize = strlen((char*)wbuf);
|
|
lfs3_file_write(&lfs3, &file, wbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
uint8_t rbuf[1024];
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
//////
|
|
|
|
// try to mount with a smaller block count
|
|
cfg = *CFG;
|
|
cfg.block_count = SMALLER_BLOCK_COUNT;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => LFS3_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',
|
|
'FORMAT_BLOCK_COUNT',
|
|
]
|
|
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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = SMALLER_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// do some work
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[1024] = "Hello World!";
|
|
lfs3_size_t wsize = strlen((char*)wbuf);
|
|
lfs3_file_write(&lfs3, &file, wbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
uint8_t rbuf[1024];
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
//////
|
|
|
|
// try to grow our filesystem
|
|
cfg = *CFG;
|
|
cfg.block_count = BIGGER_BLOCK_COUNT;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
lfs3_fs_grow(&lfs3, BIGGER_BLOCK_COUNT) => 0;
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BIGGER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// file still exists?
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
// do some work
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
|
|
strcpy((char*)wbuf, "Chris was here!");
|
|
wsize = strlen((char*)wbuf);
|
|
lfs3_file_write(&lfs3, &file, wbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// stays after a mount?
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BIGGER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// file still exists?
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 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',
|
|
'FORMAT_BLOCK_COUNT',
|
|
]
|
|
code = '''
|
|
// create a smaller fs
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = SMALLER_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// do some work
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
|
|
uint8_t wbuf[1024] = "Hello World!";
|
|
lfs3_size_t wsize = strlen((char*)wbuf);
|
|
lfs3_file_write(&lfs3, &file, wbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
uint8_t rbuf[1024];
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
//////
|
|
|
|
// try to grow to same size
|
|
cfg = *CFG;
|
|
cfg.block_count = SMALLER_BLOCK_COUNT;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
lfs3_fs_grow(&lfs3, SMALLER_BLOCK_COUNT) => 0;
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// file still exists?
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
// do some work
|
|
lfs3_file_open(&lfs3, &file, "hello",
|
|
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
|
|
strcpy((char*)wbuf, "Chris was here!");
|
|
wsize = strlen((char*)wbuf);
|
|
lfs3_file_write(&lfs3, &file, wbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
|
|
// stays after a mount?
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == SMALLER_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// file still exists?
|
|
lfs3_stat(&lfs3, "hello", &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == wsize);
|
|
lfs3_file_open(&lfs3, &file, "hello", LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, wsize) => wsize;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
|
|
|
|
# These tests try various fuzz tests while incrementally growing the
|
|
# filesystem. When encountering LFS3_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.REMOUNT = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
code = '''
|
|
// start with a small number of blocks
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// make this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
again:;
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
int err = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
continue;
|
|
|
|
grow:;
|
|
// try growing the filesystem
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs3.grm_p[0] == 0);
|
|
|
|
// check that our mkdir worked
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_open(&lfs3, &dir, name) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_grow_incr_spam_dir_fuzz]
|
|
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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_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
|
|
lfs3_size_t x = TEST_PRNG(&prng_) % N;
|
|
|
|
// create a directory here
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
int err = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_EXIST || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// insert into our sim
|
|
for (lfs3_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(lfs3_size_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else if (op == 1) {
|
|
// choose a pseudo-random entry to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// remove this directory
|
|
char name[256];
|
|
sprintf(name, "dir%03x", x);
|
|
int err = lfs3_remove(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
sim_size -= 1;
|
|
|
|
} else {
|
|
// choose a pseudo-random entry to rename, and a pseudo-random
|
|
// number to rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
for (lfs3_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(lfs3_size_t));
|
|
sim_size -= 1;
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
sim[k] = y;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
prng = prng_;
|
|
continue;
|
|
|
|
grow:;
|
|
// try growing the filesystem
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
int err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs3.grm_p[0] == 0);
|
|
|
|
// test that our directories match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
char name2[256];
|
|
sprintf(name2, "dir%03x", sim[j]);
|
|
assert(strcmp(info.name, name2) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_grow_incr_spam_file_many]
|
|
defines.REMOUNT = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
uint32_t prng = 42;
|
|
for (lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
int err = lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &file, wbuf, SIZE);
|
|
assert(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
goto grow;
|
|
}
|
|
err = lfs3_file_close(&lfs3, &file);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
prng = prng_;
|
|
continue;
|
|
|
|
grow:;
|
|
// try growing the filesystem
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that our writes worked
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_grow_incr_spam_file_fuzz]
|
|
defines.REMOUNT = [false, true]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = 1024
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_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
|
|
lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
int err = lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &file, wbuf, SIZE);
|
|
assert(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
goto grow;
|
|
}
|
|
err = lfs3_file_close(&lfs3, &file);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// insert into our sim
|
|
for (lfs3_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(lfs3_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
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
int err = lfs3_remove(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_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
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// update our sim
|
|
for (lfs3_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(lfs3_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(lfs3_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(lfs3_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 lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
int err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// check the file contents
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_grow_incr_spam_uz_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = 1024
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isstickys = malloc(N*sizeof(bool));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs3_size_t x;
|
|
bool sticky;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfs3_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs3_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_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
|
|
lfs3_size_t x = TEST_PRNG(&prng_) % N;
|
|
|
|
// already exists?
|
|
bool exist = false;
|
|
uint32_t wprng = 0;
|
|
bool sticky = true;
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
exist = true;
|
|
wprng = sim_prngs[j];
|
|
sticky = sim_isstickys[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (!exist) {
|
|
wprng = TEST_PRNG(&prng_);
|
|
sticky = true;
|
|
}
|
|
|
|
lfs3_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 = lfs3_file_open(&lfs3, &sim_files[j]->file, name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
free(sim_files[j]);
|
|
goto grow;
|
|
}
|
|
|
|
// write some initial data if we don't exist
|
|
if (!exist || sticky) {
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &sim_files[j]->file,
|
|
wbuf, SIZE);
|
|
LFS3_ASSERT(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
goto grow;
|
|
}
|
|
}
|
|
|
|
// open in our sim
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->sticky = sticky;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// insert into our sim
|
|
for (lfs3_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(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng_);
|
|
|
|
// write to the file
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &sim_files[j]->file,
|
|
wbuf, SIZE);
|
|
assert(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
int err = lfs3_file_sync(&lfs3, &sim_files[j]->file);
|
|
assert(err == 0 || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// update in our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
// no longer sticky
|
|
sim_isstickys[k] = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
// new prng
|
|
sim_files[k]->prng = wprng;
|
|
// no longer sticky
|
|
sim_files[k]->sticky = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
bool sticky = sim_files[j]->sticky;
|
|
bool zombie = sim_files[j]->zombie;
|
|
|
|
// 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
|
|
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// update our sim
|
|
if (sticky && !zombie) {
|
|
// orphaned?
|
|
bool orphan = true;
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
orphan = false;
|
|
}
|
|
}
|
|
|
|
// if we were never synced, delete from sim
|
|
if (orphan) {
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
memmove(&sim[k], &sim[k+1],
|
|
(sim_size-(k+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k], &sim_prngs[k+1],
|
|
(sim_size-(k+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
int err = lfs3_remove(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
|
|
// mark any related sim files as zombied
|
|
for (lfs3_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
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng_) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool sticky = sim_isstickys[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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// update our sim
|
|
for (lfs3_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(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng/sticky
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[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(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs3_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 lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
int err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
if (sim_isstickys[j]) {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0;
|
|
} else {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
free(sim_prngs);
|
|
free(sim_isstickys);
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|
|
[cases.test_grow_incr_spam_uzd_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = 1024
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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 lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_t lfs3;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
// set up a simulation to compare against
|
|
lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
bool *sim_isstickys = malloc(N*sizeof(bool));
|
|
bool *sim_isdirs = malloc(N*sizeof(bool));
|
|
lfs3_size_t sim_size = 0;
|
|
|
|
typedef struct sim_file {
|
|
lfs3_size_t x;
|
|
bool sticky;
|
|
bool zombie;
|
|
uint32_t prng;
|
|
lfs3_file_t file;
|
|
} sim_file_t;
|
|
sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*));
|
|
lfs3_size_t sim_file_count = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs3_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
|
|
lfs3_size_t x = TEST_PRNG(&prng_) % N;
|
|
|
|
// already exists?
|
|
bool exist = true;
|
|
uint32_t wprng = 0;
|
|
bool sticky = true;
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim[j] == x) {
|
|
if (sim_isdirs[j]) {
|
|
goto nonsense;
|
|
}
|
|
exist = true;
|
|
wprng = sim_prngs[j];
|
|
sticky = sim_isstickys[j];
|
|
break;
|
|
}
|
|
}
|
|
// choose a random seed if we don't exist
|
|
if (!exist) {
|
|
wprng = TEST_PRNG(&prng);
|
|
sticky = true;
|
|
}
|
|
|
|
lfs3_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 = lfs3_file_open(&lfs3, &sim_files[j]->file, name,
|
|
LFS3_O_RDWR | LFS3_O_CREAT);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
free(sim_files[j]);
|
|
goto grow;
|
|
}
|
|
|
|
// write some initial data if we don't exist
|
|
if (!exist || sticky) {
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &sim_files[j]->file,
|
|
wbuf, SIZE);
|
|
assert(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
free(sim_files[j]);
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
goto grow;
|
|
}
|
|
}
|
|
|
|
// open in our sim
|
|
sim_files[j]->x = x;
|
|
sim_files[j]->sticky = sticky;
|
|
sim_files[j]->zombie = false;
|
|
sim_files[j]->prng = wprng;
|
|
sim_file_count++;
|
|
|
|
// insert into our sim
|
|
for (lfs3_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(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim_size += 1;
|
|
sim[k] = x;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// write/rewrite a file?
|
|
} else if (op == 1) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
// choose a random seed
|
|
uint32_t wprng = TEST_PRNG(&prng_);
|
|
|
|
// write to the file
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t wprng_ = wprng;
|
|
for (lfs3_size_t k = 0; k < SIZE; k++) {
|
|
wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26);
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &sim_files[j]->file,
|
|
wbuf, SIZE);
|
|
assert(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
int err = lfs3_file_sync(&lfs3, &sim_files[j]->file);
|
|
assert(err == 0 || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// update sim
|
|
sim_files[j]->prng = wprng;
|
|
if (!sim_files[j]->zombie) {
|
|
// update in our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// new prng
|
|
sim_prngs[k] = wprng;
|
|
// no longer sticky
|
|
sim_isstickys[k] = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update related sim files
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
// new prng
|
|
sim_files[k]->prng = wprng;
|
|
// no longer sticky
|
|
sim_files[k]->sticky = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// close a file?
|
|
} else if (op == 2) {
|
|
if (sim_file_count == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file handle
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_file_count;
|
|
lfs3_size_t x = sim_files[j]->x;
|
|
lfs3_size_t sticky = sim_files[j]->sticky;
|
|
lfs3_size_t zombie = sim_files[j]->zombie;
|
|
|
|
// 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
|
|
lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0;
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
// clobber closed files to try to catch lingering references
|
|
memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t));
|
|
|
|
// remove from list
|
|
free(sim_files[j]);
|
|
sim_files[j] = sim_files[sim_file_count-1];
|
|
sim_file_count -= 1;
|
|
|
|
// update our sim
|
|
if (sticky && !zombie) {
|
|
// orphaned?
|
|
bool orphan = true;
|
|
for (lfs3_size_t k = 0; k < sim_file_count; k++) {
|
|
if (sim_files[k]->x == x && !sim_files[k]->zombie) {
|
|
orphan = false;
|
|
}
|
|
}
|
|
|
|
// if we were never synced, delete from sim
|
|
if (orphan) {
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (sim[k] == x) {
|
|
memmove(&sim[k], &sim[k+1],
|
|
(sim_size-(k+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k], &sim_prngs[k+1],
|
|
(sim_size-(k+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k], &sim_isstickys[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
memmove(&sim_isdirs[k], &sim_isdirs[k+1],
|
|
(sim_size-(k+1))*sizeof(bool));
|
|
sim_size -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove a file?
|
|
} else if (op == 3) {
|
|
if (sim_size == 0) {
|
|
goto nonsense;
|
|
}
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "batman%03x", x);
|
|
int err = lfs3_remove(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
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 (lfs3_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
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % sim_size;
|
|
lfs3_size_t x = sim[j];
|
|
lfs3_size_t y = TEST_PRNG(&prng_) % N;
|
|
uint32_t wprng = sim_prngs[j];
|
|
bool sticky = sim_isstickys[j];
|
|
bool dir = sim_isdirs[j];
|
|
|
|
for (lfs3_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] != dir) {
|
|
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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// update our sim
|
|
for (lfs3_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(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
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/sticky/dir
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = dir;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[j], &sim_isstickys[j+1],
|
|
(sim_size-(j+1))*sizeof(bool));
|
|
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(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
memmove(&sim_isdirs[k+1], &sim_isdirs[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
sim_isstickys[k] = sticky;
|
|
sim_isdirs[k] = dir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update any related sim files
|
|
for (lfs3_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
|
|
lfs3_size_t x = TEST_PRNG(&prng_) % N;
|
|
|
|
for (lfs3_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 = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// insert into our sim
|
|
for (lfs3_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= x) {
|
|
// insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs3_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
memmove(&sim_isstickys[k+1], &sim_isstickys[k],
|
|
(sim_size-k)*sizeof(bool));
|
|
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 (lfs3_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 lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
int err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
// check that disk matches our simulation
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
if (sim_isdirs[j]) {
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
} else if (sim_isstickys[j]) {
|
|
assert(info.type == LFS3_TYPE_STICKYNOTE);
|
|
assert(info.size == 0);
|
|
} else {
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
for (lfs3_size_t j = 0; j < sim_size; j++) {
|
|
if (sim_isdirs[j]) {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY)
|
|
=> LFS3_ERR_ISDIR;
|
|
|
|
} else {
|
|
char name[256];
|
|
sprintf(name, "batman%03x", sim[j]);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
if (sim_isstickys[j]) {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0;
|
|
} else {
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our file handles match our simulation
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
uint32_t wprng = sim_files[j]->prng;
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
}
|
|
|
|
// clean up sim/lfs3
|
|
free(sim);
|
|
free(sim_prngs);
|
|
free(sim_isstickys);
|
|
free(sim_isdirs);
|
|
for (lfs3_size_t j = 0; j < sim_file_count; j++) {
|
|
lfs3_file_close(&lfs3, &sim_files[j]->file) => 0;
|
|
free(sim_files[j]);
|
|
}
|
|
free(sim_files);
|
|
lfs3_unmount(&lfs3) => 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.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = 256
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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
|
|
lfs3_t lfs3;
|
|
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
|
|
if (err) {
|
|
// start with a small number of blocks
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
}
|
|
|
|
// keep some test state on disk to survive powerloss
|
|
typedef struct fuzz_state {
|
|
lfs3_size_t i;
|
|
uint32_t prng;
|
|
} fuzz_state_t;
|
|
fuzz_state_t state = {.i = 0, .prng = SEED};
|
|
|
|
lfs3_file_t state_file;
|
|
err = lfs3_file_open(&lfs3, &state_file, "state", LFS3_O_RDONLY);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (!err) {
|
|
lfs3_file_read(&lfs3, &state_file,
|
|
&state, sizeof(state)) => sizeof(state);
|
|
lfs3_file_close(&lfs3, &state_file) => 0;
|
|
}
|
|
|
|
uint32_t prng = state.prng;
|
|
for (lfs3_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 = lfs3_mkdir(&lfs3, "test");
|
|
assert(!err || err == LFS3_ERR_EXIST || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// how many files do we have?
|
|
lfs3_size_t count = 0;
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
count++;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || count == 0) {
|
|
// choose a pseudo-random number
|
|
lfs3_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 (lfs3_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);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
err = lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &file, wbuf, SIZE);
|
|
assert(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
goto grow;
|
|
}
|
|
err = lfs3_file_close(&lfs3, &file);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % count;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
sprintf(name, "test/%s", info.name);
|
|
err = lfs3_remove(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % count;
|
|
lfs3_size_t y = TEST_PRNG(&prng_) % N;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
}
|
|
|
|
// update our state file
|
|
state.i = i;
|
|
state.prng = prng_;
|
|
err = lfs3_file_open(&lfs3, &state_file, "state",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &state_file,
|
|
&state, sizeof(state));
|
|
assert(d == sizeof(state) || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
lfs3_file_close(&lfs3, &state_file) => 0;
|
|
goto grow;
|
|
}
|
|
err = lfs3_file_close(&lfs3, &state_file);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
prng = prng_;
|
|
continue;
|
|
|
|
grow:;
|
|
// try growing the filesystem
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that things look more-or-less ok
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
int err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// at least try to read the files
|
|
char name[256];
|
|
sprintf(name, "test/%s", info.name);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
// all data should be lowercase ascii
|
|
for (lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
ck = (ck + (rbuf[j] - 'a')) % 26;
|
|
}
|
|
assert(ck == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 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]
|
|
# note dirs x files grows O(n^2)
|
|
defines.N = [1, 2, 4, 8]
|
|
defines.M = 'N'
|
|
defines.OPS = 256
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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
|
|
lfs3_t lfs3;
|
|
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
|
|
if (err) {
|
|
// start with a small number of blocks
|
|
struct lfs3_cfg cfg = *CFG;
|
|
cfg.block_count = FORMAT_BLOCK_COUNT;
|
|
lfs3_format(&lfs3,
|
|
LFS3_F_RDWR
|
|
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
|
|
&cfg) => 0;
|
|
// mount with maximum block count
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
|
|
// fsstat up to date?
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
}
|
|
|
|
// keep some test state on disk to survive powerloss
|
|
typedef struct fuzz_state {
|
|
lfs3_size_t i;
|
|
uint32_t prng;
|
|
} fuzz_state_t;
|
|
fuzz_state_t state = {.i = 0, .prng = SEED};
|
|
|
|
lfs3_file_t state_file;
|
|
err = lfs3_file_open(&lfs3, &state_file, "state", LFS3_O_RDONLY);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (!err) {
|
|
lfs3_file_read(&lfs3, &state_file,
|
|
&state, sizeof(state)) => sizeof(state);
|
|
lfs3_file_close(&lfs3, &state_file) => 0;
|
|
}
|
|
|
|
uint32_t prng = state.prng;
|
|
for (lfs3_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 = lfs3_mkdir(&lfs3, "test");
|
|
assert(!err || err == LFS3_ERR_EXIST || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// how many dirs do we have?
|
|
lfs3_size_t dir_count = 0;
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("quartz..."));
|
|
assert(memcmp(info.name, "quartz", strlen("quartz")) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
dir_count++;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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
|
|
lfs3_size_t x = TEST_PRNG(&prng_) % N;
|
|
|
|
// create a dir here
|
|
char name[256];
|
|
sprintf(name, "test/quartz%03x", x);
|
|
err = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_EXIST || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// deleting a dir?
|
|
} else if (op == 1) {
|
|
// choose a random dir to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % dir_count;
|
|
// find the dir
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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 = lfs3_remove(&lfs3, name);
|
|
assert(!err
|
|
|| err == LFS3_ERR_NOTEMPTY
|
|
|| err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// renaming a dir?
|
|
} else {
|
|
// choose a random dir to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % dir_count;
|
|
lfs3_size_t y = TEST_PRNG(&prng_) % N;
|
|
// find the dir
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err
|
|
|| err == LFS3_ERR_NOTEMPTY
|
|
|| err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
}
|
|
|
|
// file op?
|
|
} else {
|
|
// choose a pseudo-random dir
|
|
lfs3_size_t dir_i = TEST_PRNG(&prng_) % dir_count;
|
|
// find the dir
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= dir_i; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
char dir_path[256];
|
|
sprintf(dir_path, "test/%s", info.name);
|
|
|
|
// how many files do we have?
|
|
lfs3_size_t count = 0;
|
|
lfs3_dir_open(&lfs3, &dir, dir_path) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
assert(memcmp(
|
|
info.name,
|
|
"amethyst", strlen("amethyst")) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
count++;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// creating a new file?
|
|
if (op == 3 || count == 0) {
|
|
// choose a pseudo-random number
|
|
lfs3_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 (lfs3_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);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
err = lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &file, wbuf, SIZE);
|
|
assert(d == SIZE || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
goto grow;
|
|
}
|
|
err = lfs3_file_close(&lfs3, &file);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// deleting a file?
|
|
} else if (op == 4) {
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % count;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, dir_path) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
sprintf(name, "%s/%s", dir_path, info.name);
|
|
err = lfs3_remove(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename
|
|
lfs3_size_t j = TEST_PRNG(&prng_) % count;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, dir_path) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// choose a random dir to rename to
|
|
lfs3_size_t dir_j = TEST_PRNG(&prng_) % dir_count;
|
|
// find the dir
|
|
struct lfs3_info info_;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info_) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info_) => 0;
|
|
for (lfs3_size_t k = 0; k <= dir_j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info_) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// choose a random file to rename to
|
|
lfs3_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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update our state file
|
|
state.i = i;
|
|
state.prng = prng_;
|
|
err = lfs3_file_open(&lfs3, &state_file, "state",
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
lfs3_ssize_t d = lfs3_file_write(&lfs3, &state_file,
|
|
&state, sizeof(state));
|
|
assert(d == sizeof(state) || d == LFS3_ERR_NOSPC);
|
|
if (d == LFS3_ERR_NOSPC) {
|
|
lfs3_file_close(&lfs3, &state_file) => 0;
|
|
goto grow;
|
|
}
|
|
err = lfs3_file_close(&lfs3, &state_file);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
goto grow;
|
|
}
|
|
|
|
prng = prng_;
|
|
continue;
|
|
|
|
grow:;
|
|
// try growing the filesystem
|
|
struct lfs3_fsinfo fsinfo;
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_count >= FORMAT_BLOCK_COUNT);
|
|
assert(fsinfo.block_count <= BLOCK_COUNT);
|
|
lfs3_ssize_t used = lfs3_fs_usage(&lfs3);
|
|
assert(used >= 0);
|
|
|
|
// we may need to grow multiple blocks before the system gets unstuck
|
|
lfs3_size_t block_count_ = fsinfo.block_count;
|
|
while (true) {
|
|
assert(block_count_ < BLOCK_COUNT);
|
|
block_count_ += 1;
|
|
err = lfs3_fs_grow(&lfs3, block_count_);
|
|
assert(!err || err == LFS3_ERR_NOSPC);
|
|
if (err == LFS3_ERR_NOSPC) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
printf("grew %d/%d -> %d/%d\n",
|
|
used, fsinfo.block_count,
|
|
used, block_count_);
|
|
|
|
// fsstat up to date?
|
|
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == block_count_);
|
|
assert(fsinfo.name_limit == LFS3_NAME_MAX);
|
|
assert(fsinfo.file_limit == LFS3_FILE_MAX);
|
|
|
|
goto again;
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that things look more-or-less ok
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
int err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("quartz..."));
|
|
assert(memcmp(info.name, "quartz", strlen("quartz")) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
|
|
// check that our dirs look more-or-less ok
|
|
char name[256];
|
|
sprintf(name, "test/%s", info.name);
|
|
lfs3_dir_t dir_;
|
|
lfs3_dir_open(&lfs3, &dir_, name) => 0;
|
|
struct lfs3_info info_;
|
|
lfs3_dir_read(&lfs3, &dir_, &info_) => 0;
|
|
assert(strcmp(info_.name, ".") == 0);
|
|
assert(info_.type == LFS3_TYPE_DIR);
|
|
assert(info_.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir_, &info_) => 0;
|
|
assert(strcmp(info_.name, "..") == 0);
|
|
assert(info_.type == LFS3_TYPE_DIR);
|
|
assert(info_.size == 0);
|
|
|
|
while (true) {
|
|
err = lfs3_dir_read(&lfs3, &dir_, &info_);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info_.name) == strlen("amethyst..."));
|
|
assert(memcmp(
|
|
info_.name,
|
|
"amethyst", strlen("amethyst")) == 0);
|
|
assert(info_.type == LFS3_TYPE_REG);
|
|
assert(info_.size == SIZE);
|
|
|
|
// at least try to read the files
|
|
sprintf(name, "test/%s/%s", info.name, info_.name);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
// all data should be lowercase ascii
|
|
for (lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
ck = (ck + (rbuf[j] - 'a')) % 26;
|
|
}
|
|
assert(ck == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir_) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|