littlefs/tests/test_compat.toml
Christopher Haster b72c96d440 Added support for writing on-disk version lfs2.0
The intention is to help interop with older minor versions of littlefs.

Unfortunately, since lfs2.0 drivers cannot mount lfs2.1 images, there are
situations where it would be useful to write to write strictly lfs2.0
compatible images. The solution here adds a "disk_version" configuration
option which determines the behavior of lfs2.1 dependent features.

Normally you would expect this to only change write behavior. But since the
main change in lfs2.1 increased validation of erased data, we also need to
skip this extra validation (fcrc) or see terrible slowdowns when writing.
2023-06-29 12:31:22 -05:00

1454 lines
42 KiB
C

# Test for compatibility between different littlefs versions
#
# Note, these tests are a bit special. They expect to be linked against two
# different versions of littlefs:
# - lfs => the new/current version of littlefs
# - lfsp => the previous version of littlefs
#
# If lfsp is not linked, and LFSP is not defined, these tests will alias
# the relevant lfs types/functions as necessary so at least the tests can
# themselves be tested locally.
#
# But to get value from these tests, it's expected that the previous version
# of littlefs be linked in during CI, with the help of scripts/changeprefix.py
#
# alias littlefs symbols as needed
#
# there may be a better way to do this, but oh well, explicit aliases works
code = '''
#ifdef LFSP
#define STRINGIZE(x) STRINGIZE_(x)
#define STRINGIZE_(x) #x
#include STRINGIZE(LFSP)
#else
#define LFSP_DISK_VERSION LFS_DISK_VERSION
#define LFSP_DISK_VERSION_MAJOR LFS_DISK_VERSION_MAJOR
#define LFSP_DISK_VERSION_MINOR LFS_DISK_VERSION_MINOR
#define lfsp_t lfs_t
#define lfsp_config lfs_config
#define lfsp_format lfs_format
#define lfsp_mount lfs_mount
#define lfsp_unmount lfs_unmount
#define lfsp_fsinfo lfs_fsinfo
#define lfsp_fs_stat lfs_fs_stat
#define lfsp_dir_t lfs_dir_t
#define lfsp_info lfs_info
#define LFSP_TYPE_REG LFS_TYPE_REG
#define LFSP_TYPE_DIR LFS_TYPE_DIR
#define lfsp_mkdir lfs_mkdir
#define lfsp_dir_open lfs_dir_open
#define lfsp_dir_read lfs_dir_read
#define lfsp_dir_close lfs_dir_close
#define lfsp_file_t lfs_file_t
#define LFSP_O_RDONLY LFS_O_RDONLY
#define LFSP_O_WRONLY LFS_O_WRONLY
#define LFSP_O_CREAT LFS_O_CREAT
#define LFSP_O_EXCL LFS_O_EXCL
#define LFSP_SEEK_SET LFS_SEEK_SET
#define lfsp_file_open lfs_file_open
#define lfsp_file_write lfs_file_write
#define lfsp_file_read lfs_file_read
#define lfsp_file_seek lfs_file_seek
#define lfsp_file_close lfs_file_close
#endif
'''
## forward-compatibility tests ##
# test we can mount in a new version
[cases.test_compat_forward_mount]
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// confirm the previous mount works
lfsp_mount(&lfsp, &cfgp) => 0;
lfsp_unmount(&lfsp) => 0;
// now test the new mount
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
lfs_unmount(&lfs) => 0;
'''
# test we can read dirs in a new version
[cases.test_compat_forward_read_dirs]
defines.COUNT = 5
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT dirs
lfsp_mount(&lfsp, &cfgp) => 0;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0;
'''
# test we can read files in a new version
[cases.test_compat_forward_read_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT files
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// can we list the files?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
# test we can read files in dirs in a new version
[cases.test_compat_forward_read_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT files+dirs
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
lfsp_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, name) => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
# test we can write dirs in a new version
[cases.test_compat_forward_write_dirs]
defines.COUNT = 10
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT/2 dirs
lfsp_mount(&lfsp, &cfgp) => 0;
for (lfs_size_t i = 0; i < COUNT/2; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// write another COUNT/2 dirs
for (lfs_size_t i = COUNT/2; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
}
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0;
'''
# test we can write files in a new version
[cases.test_compat_forward_write_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write half COUNT files
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// write half
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name, LFS_O_WRONLY) => 0;
lfs_file_seek(&lfs, &file, SIZE/2, LFS_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
// can we list the files?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
# test we can write files in dirs in a new version
[cases.test_compat_forward_write_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write half COUNT files
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
// write half
lfsp_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfs_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name, LFS_O_WRONLY) => 0;
lfs_file_seek(&lfs, &file, SIZE/2, LFS_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, name) => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
## backwards-compatibility tests ##
# test we can mount in an old version
[cases.test_compat_backward_mount]
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// confirm the new mount works
lfs_mount(&lfs, cfg) => 0;
lfs_unmount(&lfs) => 0;
// now test the previous mount
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
lfsp_unmount(&lfsp) => 0;
'''
# test we can read dirs in an old version
[cases.test_compat_backward_read_dirs]
defines.COUNT = 5
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT dirs
lfs_mount(&lfs, cfg) => 0;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the new version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
lfsp_unmount(&lfsp) => 0;
'''
# test we can read files in an old version
[cases.test_compat_backward_read_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT files
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// can we list the files?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
# test we can read files in dirs in an old version
[cases.test_compat_backward_read_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT files+dirs
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
lfs_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, name) => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
# test we can write dirs in an old version
[cases.test_compat_backward_write_dirs]
defines.COUNT = 10
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT/2 dirs
lfs_mount(&lfs, cfg) => 0;
for (lfs_size_t i = 0; i < COUNT/2; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// write another COUNT/2 dirs
for (lfs_size_t i = COUNT/2; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
}
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
lfsp_unmount(&lfsp) => 0;
'''
# test we can write files in an old version
[cases.test_compat_backward_write_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write half COUNT files
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// write half
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfs_unmount(&lfs) => 0;
// mount the new version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_WRONLY) => 0;
lfsp_file_seek(&lfsp, &file, SIZE/2, LFSP_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
// can we list the files?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
# test we can write files in dirs in an old version
[cases.test_compat_backward_write_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write half COUNT files
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
// write half
lfs_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfs_unmount(&lfs) => 0;
// mount the new version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfsp_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_WRONLY) => 0;
lfsp_file_seek(&lfsp, &file, SIZE/2, LFSP_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, name) => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
## incompatiblity tests ##
# test that we fail to mount after a major version bump
[cases.test_compat_major_incompat]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// bump the major version
//
// note we're messing around with internals to do this! this
// is not a user API
lfs_mount(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION + 0x00010000,
.block_size = lfs.cfg->block_size,
.block_count = lfs.cfg->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, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})) => 0;
lfs_unmount(&lfs) => 0;
// mount should now fail
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
'''
# test that we fail to mount after a minor version bump
[cases.test_compat_minor_incompat]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// bump the minor version
//
// note we're messing around with internals to do this! this
// is not a user API
lfs_mount(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION + 0x00000001,
.block_size = lfs.cfg->block_size,
.block_count = lfs.cfg->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, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})) => 0;
lfs_unmount(&lfs) => 0;
// mount should now fail
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
'''
# test that we correctly bump the minor version
[cases.test_compat_minor_bump]
in = 'lfs.c'
if = '''
LFS_DISK_VERSION_MINOR > 0
&& DISK_VERSION == 0
'''
code = '''
// create a superblock
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "test",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_file_write(&lfs, &file, "testtest", 8) => 8;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// write an old minor version
//
// note we're messing around with internals to do this! this
// is not a user API
lfs_mount(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION - 0x00000001,
.block_size = lfs.cfg->block_size,
.block_count = lfs.cfg->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, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})) => 0;
lfs_unmount(&lfs) => 0;
// mount should still work
lfs_mount(&lfs, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
uint8_t buffer[8];
lfs_file_read(&lfs, &file, buffer, 8) => 8;
assert(memcmp(buffer, "testtest", 8) == 0);
lfs_file_close(&lfs, &file) => 0;
// minor version should be unchanged
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
lfs_unmount(&lfs) => 0;
// if we write, we need to bump the minor version
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
lfs_file_open(&lfs, &file, "test", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_write(&lfs, &file, "teeeeest", 8) => 8;
lfs_file_close(&lfs, &file) => 0;
// minor version should be changed
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
lfs_unmount(&lfs) => 0;
// and of course mount should still work
lfs_mount(&lfs, cfg) => 0;
// minor version should have changed
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, 8) => 8;
assert(memcmp(buffer, "teeeeest", 8) == 0);
lfs_file_close(&lfs, &file) => 0;
// yep, still changed
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
lfs_unmount(&lfs) => 0;
'''