34b5b39551
The initial implementation for this was provided by kaetemi, originally as a mount flag. However, it has been modified here to be self-contained in an explicit runtime function that can be called after mount. The reasons for an explicit function: 1. lfs_mount stays a strictly readonly operation, and avoids pulling in all of the write machinery. 2. filesystem-wide operations such as lfs_fs_grow can be a bit risky, and irreversable. The action of growing the filesystem should be very intentional. --- One concern with this change is that this will be the first function that changes metadata in the superblock. This might break tools that expect the first valid superblock entry to contain the most recent metadata.
667 lines
20 KiB
TOML
667 lines
20 KiB
TOML
# simple formatting test
|
|
[cases.test_superblocks_format]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
'''
|
|
|
|
# mount/unmount
|
|
[cases.test_superblocks_mount]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# reentrant format
|
|
[cases.test_superblocks_reentrant_format]
|
|
reentrant = true
|
|
code = '''
|
|
lfs_t lfs;
|
|
int err = lfs_mount(&lfs, cfg);
|
|
if (err) {
|
|
lfs_format(&lfs, cfg) => 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
}
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# invalid mount
|
|
[cases.test_superblocks_invalid_mount]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
'''
|
|
|
|
# expanding superblock
|
|
[cases.test_superblocks_expand]
|
|
defines.BLOCK_CYCLES = [32, 33, 1]
|
|
defines.N = [10, 100, 1000]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
for (int i = 0; i < N; i++) {
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "dummy",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
struct lfs_info info;
|
|
lfs_stat(&lfs, "dummy", &info) => 0;
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
lfs_remove(&lfs, "dummy") => 0;
|
|
}
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// one last check after power-cycle
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "dummy",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
struct lfs_info info;
|
|
lfs_stat(&lfs, "dummy", &info) => 0;
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# expanding superblock with power cycle
|
|
[cases.test_superblocks_expand_power_cycle]
|
|
defines.BLOCK_CYCLES = [32, 33, 1]
|
|
defines.N = [10, 100, 1000]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
for (int i = 0; i < N; i++) {
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
// remove lingering dummy?
|
|
struct lfs_info info;
|
|
int err = lfs_stat(&lfs, "dummy", &info);
|
|
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
|
|
if (!err) {
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
lfs_remove(&lfs, "dummy") => 0;
|
|
}
|
|
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "dummy",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
lfs_stat(&lfs, "dummy", &info) => 0;
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
lfs_unmount(&lfs) => 0;
|
|
}
|
|
|
|
// one last check after power-cycle
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
struct lfs_info info;
|
|
lfs_stat(&lfs, "dummy", &info) => 0;
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# reentrant expanding superblock
|
|
[cases.test_superblocks_reentrant_expand]
|
|
defines.BLOCK_CYCLES = [2, 1]
|
|
defines.N = 24
|
|
reentrant = true
|
|
code = '''
|
|
lfs_t lfs;
|
|
int err = lfs_mount(&lfs, cfg);
|
|
if (err) {
|
|
lfs_format(&lfs, cfg) => 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
}
|
|
|
|
for (int i = 0; i < N; i++) {
|
|
// remove lingering dummy?
|
|
struct lfs_info info;
|
|
err = lfs_stat(&lfs, "dummy", &info);
|
|
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
|
|
if (!err) {
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
lfs_remove(&lfs, "dummy") => 0;
|
|
}
|
|
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "dummy",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
lfs_stat(&lfs, "dummy", &info) => 0;
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
}
|
|
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// one last check after power-cycle
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
struct lfs_info info;
|
|
lfs_stat(&lfs, "dummy", &info) => 0;
|
|
assert(strcmp(info.name, "dummy") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# mount with unknown block_size/block_count
|
|
[cases.test_superblocks_unknowns]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
|
|
// known block_size/block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = ERASE_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_count/block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// do some work
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "test",
|
|
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
|
|
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
|
|
uint8_t buffer[256];
|
|
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
assert(memcmp(buffer, "hello!", 6) == 0);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# mount with blocks larger than the erase_size
|
|
[cases.test_superblocks_larger_blocks]
|
|
defines.BLOCK_SIZE = [
|
|
'2*ERASE_SIZE',
|
|
'4*ERASE_SIZE',
|
|
'(ERASE_COUNT/2)*ERASE_SIZE']
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
|
|
// known block_size/block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// incorrect block_size
|
|
cfg->block_size = ERASE_SIZE;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
|
|
// unknown block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = ERASE_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_count/block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// do some work
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "test",
|
|
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
|
|
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
|
|
uint8_t buffer[256];
|
|
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
assert(memcmp(buffer, "hello!", 6) == 0);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# mount with blocks smaller than the erase_size
|
|
[cases.test_superblocks_smaller_blocks]
|
|
defines.FORMAT_BLOCK_SIZE = 'ERASE_SIZE/2'
|
|
defines.FORMAT_BLOCK_COUNT = 'BLOCK_COUNT * (BLOCK_SIZE/FORMAT_BLOCK_SIZE)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
lfs.block_size = BLOCK_SIZE;
|
|
lfs.block_count = BLOCK_COUNT;
|
|
|
|
lfs_mdir_t root = {
|
|
.pair = {0, 0}, // make sure this goes into block 0
|
|
.rev = 0,
|
|
.off = sizeof(uint32_t),
|
|
.etag = 0xffffffff,
|
|
.count = 0,
|
|
.tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
|
.erased = false,
|
|
.split = false,
|
|
};
|
|
|
|
lfs_superblock_t superblock = {
|
|
.version = LFS_DISK_VERSION,
|
|
.block_size = FORMAT_BLOCK_SIZE,
|
|
.block_count = FORMAT_BLOCK_COUNT,
|
|
.name_max = LFS_NAME_MAX,
|
|
.file_max = LFS_FILE_MAX,
|
|
.attr_max = LFS_ATTR_MAX,
|
|
};
|
|
|
|
lfs_superblock_tole32(&superblock);
|
|
lfs_dir_commit(&lfs, &root, LFS_MKATTRS(
|
|
{LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
|
|
{LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
|
|
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
|
|
&superblock})) => 0;
|
|
|
|
// known block_size/block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
|
|
// unknown block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
|
|
// unknown block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = ERASE_COUNT;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
|
|
// unknown block_count/block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
'''
|
|
|
|
# mount with blocks fewer than the erase_count
|
|
[cases.test_superblocks_fewer_blocks]
|
|
defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
|
|
// known block_size/block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// incorrect block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = ERASE_COUNT;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
|
|
// unknown block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_count/block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// do some work
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "test",
|
|
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
|
|
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
|
|
uint8_t buffer[256];
|
|
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
assert(memcmp(buffer, "hello!", 6) == 0);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# mount with more blocks than the erase_count
|
|
[cases.test_superblocks_more_blocks]
|
|
defines.FORMAT_BLOCK_COUNT = '2*ERASE_COUNT'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
lfs.block_size = BLOCK_SIZE;
|
|
lfs.block_count = BLOCK_COUNT;
|
|
|
|
lfs_mdir_t root = {
|
|
.pair = {0, 0}, // make sure this goes into block 0
|
|
.rev = 0,
|
|
.off = sizeof(uint32_t),
|
|
.etag = 0xffffffff,
|
|
.count = 0,
|
|
.tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
|
.erased = false,
|
|
.split = false,
|
|
};
|
|
|
|
lfs_superblock_t superblock = {
|
|
.version = LFS_DISK_VERSION,
|
|
.block_size = BLOCK_SIZE,
|
|
.block_count = FORMAT_BLOCK_COUNT,
|
|
.name_max = LFS_NAME_MAX,
|
|
.file_max = LFS_FILE_MAX,
|
|
.attr_max = LFS_ATTR_MAX,
|
|
};
|
|
|
|
lfs_superblock_tole32(&superblock);
|
|
lfs_dir_commit(&lfs, &root, LFS_MKATTRS(
|
|
{LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
|
|
{LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
|
|
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
|
|
&superblock})) => 0;
|
|
|
|
// known block_size/block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
|
|
// unknown block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = ERASE_COUNT;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
'''
|
|
|
|
# the real challenge is finding superblock 1 when block_size is unknown,
|
|
# test for this explicitly
|
|
[cases.test_superblocks_unknown_superblock1]
|
|
in = 'lfs.c'
|
|
defines.BLOCK_SIZE = [
|
|
'ERASE_SIZE',
|
|
'2*ERASE_SIZE',
|
|
'4*ERASE_SIZE',
|
|
'(ERASE_COUNT/2)*ERASE_SIZE']
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
lfs.block_size = BLOCK_SIZE;
|
|
lfs.block_count = BLOCK_COUNT;
|
|
|
|
uint8_t buffer[lfs_max(READ_SIZE, sizeof(uint32_t))];
|
|
memset(buffer, 0, lfs_max(READ_SIZE, sizeof(uint32_t)));
|
|
cfg->read(cfg, 0, 0, buffer, lfs_max(READ_SIZE, sizeof(uint32_t))) => 0;
|
|
uint32_t rev;
|
|
memcpy(&rev, buffer, sizeof(uint32_t));
|
|
rev = lfs_fromle32(rev);
|
|
|
|
lfs_mdir_t root = {
|
|
.pair = {1, 1}, // make sure this goes into block 1
|
|
.rev = rev,
|
|
.off = sizeof(uint32_t),
|
|
.etag = 0xffffffff,
|
|
.count = 0,
|
|
.tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
|
.erased = false,
|
|
.split = false,
|
|
};
|
|
|
|
lfs_superblock_t superblock = {
|
|
.version = LFS_DISK_VERSION,
|
|
.block_size = BLOCK_SIZE,
|
|
.block_count = BLOCK_COUNT,
|
|
.name_max = LFS_NAME_MAX,
|
|
.file_max = LFS_FILE_MAX,
|
|
.attr_max = LFS_ATTR_MAX,
|
|
};
|
|
|
|
lfs_superblock_tole32(&superblock);
|
|
lfs_dir_commit(&lfs, &root, LFS_MKATTRS(
|
|
{LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
|
|
{LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
|
|
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
|
|
&superblock})) => 0;
|
|
|
|
// known block_size/block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_count
|
|
cfg->block_size = BLOCK_SIZE;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = ERASE_COUNT;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// unknown block_count/block_size
|
|
cfg->block_size = 0;
|
|
cfg->block_count = 0;
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# mount and grow the filesystem
|
|
[cases.test_superblocks_grow]
|
|
defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
|
|
defines.BLOCK_COUNT_2 = 'ERASE_COUNT'
|
|
defines.KNOWN_BLOCK_COUNT = [true, false]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_format(&lfs, cfg) => 0;
|
|
|
|
if (KNOWN_BLOCK_COUNT) {
|
|
cfg->block_count = BLOCK_COUNT;
|
|
} else {
|
|
cfg->block_count = 0;
|
|
}
|
|
|
|
// mount with block_size < erase_size
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
struct lfs_fsinfo fsinfo;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// same size is a noop
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_grow(&lfs, BLOCK_COUNT) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// grow to new size
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
if (KNOWN_BLOCK_COUNT) {
|
|
cfg->block_count = BLOCK_COUNT_2;
|
|
} else {
|
|
cfg->block_count = 0;
|
|
}
|
|
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// mounting with the previous size should fail
|
|
cfg->block_count = BLOCK_COUNT;
|
|
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
|
|
|
if (KNOWN_BLOCK_COUNT) {
|
|
cfg->block_count = BLOCK_COUNT_2;
|
|
} else {
|
|
cfg->block_count = 0;
|
|
}
|
|
|
|
// same size is a noop
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
// do some work
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
|
lfs_file_t file;
|
|
lfs_file_open(&lfs, &file, "test",
|
|
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
|
|
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
lfs_unmount(&lfs) => 0;
|
|
|
|
lfs_mount(&lfs, cfg) => 0;
|
|
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
|
assert(fsinfo.block_size == BLOCK_SIZE);
|
|
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
|
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
|
|
uint8_t buffer[256];
|
|
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
|
|
lfs_file_close(&lfs, &file) => 0;
|
|
assert(memcmp(buffer, "hello!", 6) == 0);
|
|
lfs_unmount(&lfs) => 0;
|
|
'''
|