Merge pull request #848 from littlefs-project/devel

Minor release: v2.7
This commit is contained in:
Christopher Haster 2023-06-30 12:33:10 -05:00 committed by GitHub
commit 611c9b20db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 451 additions and 91 deletions

View File

@ -102,7 +102,7 @@ jobs:
# sizes table
i=0
j=0
for c in "" readonly threadsafe migrate error-asserts
for c in "" readonly threadsafe multiversion migrate error-asserts
do
# per-config results
c_or_default=${c:-default}

View File

@ -170,6 +170,27 @@ jobs:
cp lfs.data.csv sizes/${{matrix.arch}}-threadsafe.data.csv
cp lfs.stack.csv sizes/${{matrix.arch}}-threadsafe.stack.csv
cp lfs.structs.csv sizes/${{matrix.arch}}-threadsafe.structs.csv
- name: sizes-multiversion
run: |
make clean
CFLAGS="$CFLAGS \
-DLFS_NO_ASSERT \
-DLFS_NO_DEBUG \
-DLFS_NO_WARN \
-DLFS_NO_ERROR \
-DLFS_MULTIVERSION" \
make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv
./scripts/structs.py -u lfs.structs.csv
./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \
-bfunction \
-fcode=code_size \
-fdata=data_size \
-fstack=stack_limit --max=stack_limit
mkdir -p sizes
cp lfs.code.csv sizes/${{matrix.arch}}-multiversion.code.csv
cp lfs.data.csv sizes/${{matrix.arch}}-multiversion.data.csv
cp lfs.stack.csv sizes/${{matrix.arch}}-multiversion.stack.csv
cp lfs.structs.csv sizes/${{matrix.arch}}-multiversion.structs.csv
- name: sizes-migrate
run: |
make clean
@ -353,6 +374,42 @@ jobs:
run: |
CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test
# run LFS_MULTIVERSION tests
test-multiversion:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: install
run: |
# need a few things
sudo apt-get update -qq
sudo apt-get install -qq gcc python3 python3-pip
pip3 install toml
gcc --version
python3 --version
- name: test-multiversion
run: |
CFLAGS="$CFLAGS -DLFS_MULTIVERSION" make test
# run tests on the older version lfs2.0
test-lfs2_0:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: install
run: |
# need a few things
sudo apt-get update -qq
sudo apt-get install -qq gcc python3 python3-pip
pip3 install toml
gcc --version
python3 --version
- name: test-lfs2_0
run: |
CFLAGS="$CFLAGS -DLFS_MULTIVERSION" \
TESTFLAGS="$TESTFLAGS -DDISK_VERSION=0x00020000" \
make test
# run under Valgrind to check for memory errors
test-valgrind:
runs-on: ubuntu-22.04
@ -371,7 +428,8 @@ jobs:
# on one geometry
- name: test-valgrind
run: |
TESTFLAGS="$TESTFLAGS --valgrind -Gdefault -Pnone" make test
TESTFLAGS="$TESTFLAGS --valgrind --context=1024 -Gdefault -Pnone" \
make test
# test that compilation is warning free under clang
# run with Clang, mostly to check for Clang-specific warnings
@ -684,7 +742,7 @@ jobs:
# sizes table
i=0
j=0
for c in "" readonly threadsafe migrate error-asserts
for c in "" readonly threadsafe multiversion migrate error-asserts
do
# per-config results
c_or_default=${c:-default}

View File

@ -250,6 +250,10 @@ License Identifiers that are here available: http://spdx.org/licenses/
MCUs. It offers static wear-leveling and power-resilience with only a fixed
_O(|address|)_ pointer structure stored on each block and in RAM.
- [ChaN's FatFs] - A lightweight reimplementation of the infamous FAT filesystem
for microcontroller-scale devices. Due to limitations of FAT it can't provide
power-loss resilience, but it does allow easy interop with PCs.
- [chamelon] - A pure-OCaml implementation of (most of) littlefs, designed for
use with the MirageOS library operating system project. It is interoperable
with the reference implementation, with some caveats.
@ -266,6 +270,7 @@ License Identifiers that are here available: http://spdx.org/licenses/
[LittleFileSystem]: https://os.mbed.com/docs/mbed-os/latest/apis/littlefilesystem.html
[SPIFFS]: https://github.com/pellepl/spiffs
[Dhara]: https://github.com/dlbeer/dhara
[ChaN's FatFs]: http://elm-chan.org/fsw/ff/00index_e.html
[littlefs-python]: https://pypi.org/project/littlefs-python/
[littlefs2-rust]: https://crates.io/crates/littlefs2
[chamelon]: https://github.com/yomimono/chamelon

202
lfs.c
View File

@ -415,11 +415,11 @@ static inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) {
static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) {
return lfs_tag_type1(a->tag);
}
#endif
static inline bool lfs_gstate_needssuperblock(const lfs_gstate_t *a) {
return lfs_tag_size(a->tag) >> 9;
}
#endif
static inline bool lfs_gstate_hasmovehere(const lfs_gstate_t *a,
const lfs_block_t *pair) {
@ -518,6 +518,28 @@ static void lfs_mlist_append(lfs_t *lfs, struct lfs_mlist *mlist) {
lfs->mlist = mlist;
}
// some other filesystem operations
static uint32_t lfs_fs_disk_version(lfs_t *lfs) {
(void)lfs;
#ifdef LFS_MULTIVERSION
if (lfs->cfg->disk_version) {
return lfs->cfg->disk_version;
} else
#endif
{
return LFS_DISK_VERSION;
}
}
static uint16_t lfs_fs_disk_version_major(lfs_t *lfs) {
return 0xffff & (lfs_fs_disk_version(lfs) >> 16);
}
static uint16_t lfs_fs_disk_version_minor(lfs_t *lfs) {
return 0xffff & (lfs_fs_disk_version(lfs) >> 0);
}
/// Internal operations predeclared here ///
#ifndef LFS_READONLY
@ -535,7 +557,6 @@ static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file);
static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file);
static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss);
static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock);
static int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans);
static void lfs_fs_prepmove(lfs_t *lfs,
uint16_t id, const lfs_block_t pair[2]);
@ -546,6 +567,8 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2],
static int lfs_fs_forceconsistency(lfs_t *lfs);
#endif
static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock);
#ifdef LFS_MIGRATE
static int lfs1_traverse(lfs_t *lfs,
int (*cb)(void*, lfs_block_t), void *data);
@ -1110,7 +1133,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// next commit not yet programmed?
if (!lfs_tag_isvalid(tag)) {
maybeerased = true;
// we only might be erased if the last tag was a crc
maybeerased = (lfs_tag_type2(ptag) == LFS_TYPE_CCRC);
break;
// out of range?
} else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
@ -1155,14 +1179,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
dir->tail[1] = temptail[1];
dir->split = tempsplit;
// reset crc
// reset crc, hasfcrc
crc = 0xffffffff;
continue;
}
// fcrc is only valid when last tag was a crc
hasfcrc = false;
// crc the entry first, hopefully leaving it in the cache
err = lfs_bd_crc(lfs,
NULL, &lfs->rcache, lfs->cfg->block_size,
@ -1256,20 +1277,33 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// did we end on a valid commit? we may have an erased block
dir->erased = false;
if (maybeerased && hasfcrc && dir->off % lfs->cfg->prog_size == 0) {
// check for an fcrc matching the next prog's erased state, if
// this failed most likely a previous prog was interrupted, we
// need a new erase
uint32_t fcrc_ = 0xffffffff;
int err = lfs_bd_crc(lfs,
NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], dir->off, fcrc.size, &fcrc_);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
if (maybeerased && dir->off % lfs->cfg->prog_size == 0) {
#ifdef LFS_MULTIVERSION
// note versions < lfs2.1 did not have fcrc tags, if
// we're < lfs2.1 treat missing fcrc as erased data
//
// we don't strictly need to do this, but otherwise writing
// to lfs2.0 disks becomes very inefficient
if (lfs_fs_disk_version(lfs) < 0x00020001) {
dir->erased = true;
// found beginning of erased part?
dir->erased = (fcrc_ == fcrc.crc);
} else
#endif
if (hasfcrc) {
// check for an fcrc matching the next prog's erased state, if
// this failed most likely a previous prog was interrupted, we
// need a new erase
uint32_t fcrc_ = 0xffffffff;
int err = lfs_bd_crc(lfs,
NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], dir->off, fcrc.size, &fcrc_);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
// found beginning of erased part?
dir->erased = (fcrc_ == fcrc.crc);
}
}
// synthetic move
@ -1605,22 +1639,34 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
return err;
}
// find the expected fcrc, don't bother avoiding a reread
// of the eperturb, it should still be in our cache
struct lfs_fcrc fcrc = {.size=lfs->cfg->prog_size, .crc=0xffffffff};
err = lfs_bd_crc(lfs,
NULL, &lfs->rcache, lfs->cfg->prog_size,
commit->block, noff, fcrc.size, &fcrc.crc);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
#ifdef LFS_MULTIVERSION
// unfortunately fcrcs break mdir fetching < lfs2.1, so only write
// these if we're a >= lfs2.1 filesystem
if (lfs_fs_disk_version(lfs) <= 0x00020000) {
// don't write fcrc
} else
#endif
{
// find the expected fcrc, don't bother avoiding a reread
// of the eperturb, it should still be in our cache
struct lfs_fcrc fcrc = {
.size = lfs->cfg->prog_size,
.crc = 0xffffffff
};
err = lfs_bd_crc(lfs,
NULL, &lfs->rcache, lfs->cfg->prog_size,
commit->block, noff, fcrc.size, &fcrc.crc);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
lfs_fcrc_tole32(&fcrc);
err = lfs_dir_commitattr(lfs, commit,
LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)),
&fcrc);
if (err) {
return err;
lfs_fcrc_tole32(&fcrc);
err = lfs_dir_commitattr(lfs, commit,
LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)),
&fcrc);
if (err) {
return err;
}
}
}
@ -4051,6 +4097,15 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->cfg = cfg;
int err = 0;
#ifdef LFS_MULTIVERSION
// this driver only supports minor version < current minor version
LFS_ASSERT(!lfs->cfg->disk_version || (
(0xffff & (lfs->cfg->disk_version >> 16))
== LFS_DISK_VERSION_MAJOR
&& (0xffff & (lfs->cfg->disk_version >> 0))
<= LFS_DISK_VERSION_MINOR));
#endif
// check that bool is a truthy-preserving type
//
// note the most common reason for this failure is a before-c99 compiler,
@ -4208,7 +4263,7 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
// write one superblock
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION,
.version = lfs_fs_disk_version(lfs),
.block_size = lfs->cfg->block_size,
.block_count = lfs->cfg->block_count,
.name_max = lfs->name_max,
@ -4306,12 +4361,14 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
// check version
uint16_t major_version = (0xffff & (superblock.version >> 16));
uint16_t minor_version = (0xffff & (superblock.version >> 0));
if ((major_version != LFS_DISK_VERSION_MAJOR ||
minor_version > LFS_DISK_VERSION_MINOR)) {
if (major_version != lfs_fs_disk_version_major(lfs)
|| minor_version > lfs_fs_disk_version_minor(lfs)) {
LFS_ERROR("Invalid version "
"v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16,
major_version, minor_version,
LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
major_version,
minor_version,
lfs_fs_disk_version_major(lfs),
lfs_fs_disk_version_minor(lfs));
err = LFS_ERR_INVAL;
goto cleanup;
}
@ -4319,16 +4376,16 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
// found older minor version? set an in-device only bit in the
// gstate so we know we need to rewrite the superblock before
// the first write
if (minor_version < LFS_DISK_VERSION_MINOR) {
if (minor_version < lfs_fs_disk_version_minor(lfs)) {
LFS_DEBUG("Found older minor version "
"v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
major_version, minor_version,
LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
#ifndef LFS_READONLY
major_version,
minor_version,
lfs_fs_disk_version_major(lfs),
lfs_fs_disk_version_minor(lfs));
// note this bit is reserved on disk, so fetching more gstate
// will not interfere here
lfs_fs_prepsuperblock(lfs, true);
#endif
}
// check superblock configuration
@ -4421,6 +4478,42 @@ static int lfs_rawunmount(lfs_t *lfs) {
/// Filesystem filesystem operations ///
static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
// if the superblock is up-to-date, we must be on the most recent
// minor version of littlefs
if (!lfs_gstate_needssuperblock(&lfs->gstate)) {
fsinfo->disk_version = lfs_fs_disk_version(lfs);
// otherwise we need to read the minor version on disk
} else {
// fetch the superblock
lfs_mdir_t dir;
int err = lfs_dir_fetch(lfs, &dir, lfs->root);
if (err) {
return err;
}
lfs_superblock_t superblock;
lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock);
if (tag < 0) {
return tag;
}
lfs_superblock_fromle32(&superblock);
// read the on-disk version
fsinfo->disk_version = superblock.version;
}
// other on-disk configuration, we cache all of these for internal use
fsinfo->name_max = lfs->name_max;
fsinfo->file_max = lfs->file_max;
fsinfo->attr_max = lfs->attr_max;
return 0;
}
int lfs_fs_rawtraverse(lfs_t *lfs,
int (*cb)(void *data, lfs_block_t block), void *data,
bool includeorphans) {
@ -4631,12 +4724,10 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
}
#endif
#ifndef LFS_READONLY
static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock) {
lfs->gstate.tag = (lfs->gstate.tag & ~LFS_MKTAG(0, 0, 0x200))
| (uint32_t)needssuperblock << 9;
}
#endif
#ifndef LFS_READONLY
static int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) {
@ -4678,7 +4769,7 @@ static int lfs_fs_desuperblock(lfs_t *lfs) {
// write a new superblock
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION,
.version = lfs_fs_disk_version(lfs),
.block_size = lfs->cfg->block_size,
.block_count = lfs->cfg->block_count,
.name_max = lfs->name_max,
@ -4934,6 +5025,7 @@ static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) {
return size;
}
#ifdef LFS_MIGRATE
////// Migration from littelfs v1 below this //////
@ -6053,6 +6145,20 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
return err;
}
int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
int err = LFS_LOCK(lfs->cfg);
if (err) {
return err;
}
LFS_TRACE("lfs_fs_stat(%p, %p)", (void*)lfs, (void*)fsinfo);
err = lfs_fs_rawstat(lfs, fsinfo);
LFS_TRACE("lfs_fs_stat -> %d", err);
LFS_UNLOCK(lfs->cfg);
return err;
}
lfs_ssize_t lfs_fs_size(lfs_t *lfs) {
int err = LFS_LOCK(lfs->cfg);
if (err) {

31
lfs.h
View File

@ -21,7 +21,7 @@ extern "C"
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00020006
#define LFS_VERSION 0x00020007
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
@ -263,6 +263,14 @@ struct lfs_config {
// can help bound the metadata compaction time. Must be <= block_size.
// Defaults to block_size when zero.
lfs_size_t metadata_max;
#ifdef LFS_MULTIVERSION
// On-disk version to use when writing in the form of 16-bit major version
// + 16-bit minor version. This limiting metadata to what is supported by
// older minor versions. Note that some features will be lost. Defaults to
// to the most recent minor version when zero.
uint32_t disk_version;
#endif
};
// File info structure
@ -280,6 +288,21 @@ struct lfs_info {
char name[LFS_NAME_MAX+1];
};
// Filesystem info structure
struct lfs_fsinfo {
// On-disk version.
uint32_t disk_version;
// Upper limit on the length of file names in bytes.
lfs_size_t name_max;
// Upper limit on the size of files in bytes.
lfs_size_t file_max;
// Upper limit on the size of custom attributes in bytes.
lfs_size_t attr_max;
};
// Custom attribute structure, used to describe custom attributes
// committed atomically during file writes.
struct lfs_attr {
@ -659,6 +682,12 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
/// Filesystem-level filesystem operations
// Find on-disk info about the filesystem
//
// Fills out the fsinfo structure based on the filesystem found on-disk.
// Returns a negative error code on failure.
int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo);
// Finds the current size of the filesystem
//
// Note: Result is best effort. If files share COW structures, the returned

View File

@ -1346,6 +1346,9 @@ static void run_powerloss_none(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
@ -1415,6 +1418,9 @@ static void run_powerloss_linear(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
@ -1501,6 +1507,9 @@ static void run_powerloss_log(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
@ -1585,6 +1594,9 @@ static void run_powerloss_cycles(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
@ -1767,6 +1779,9 @@ static void run_powerloss_exhaustive(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {

View File

@ -91,6 +91,7 @@ intmax_t test_define(size_t define);
#define ERASE_CYCLES_i 8
#define BADBLOCK_BEHAVIOR_i 9
#define POWERLOSS_BEHAVIOR_i 10
#define DISK_VERSION_i 11
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
@ -103,6 +104,7 @@ intmax_t test_define(size_t define);
#define ERASE_CYCLES TEST_DEFINE(ERASE_CYCLES_i)
#define BADBLOCK_BEHAVIOR TEST_DEFINE(BADBLOCK_BEHAVIOR_i)
#define POWERLOSS_BEHAVIOR TEST_DEFINE(POWERLOSS_BEHAVIOR_i)
#define DISK_VERSION TEST_DEFINE(DISK_VERSION_i)
#define TEST_IMPLICIT_DEFINES \
TEST_DEF(READ_SIZE, PROG_SIZE) \
@ -115,9 +117,10 @@ intmax_t test_define(size_t define);
TEST_DEF(ERASE_VALUE, 0xff) \
TEST_DEF(ERASE_CYCLES, 0) \
TEST_DEF(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR) \
TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP)
TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP) \
TEST_DEF(DISK_VERSION, 0)
#define TEST_IMPLICIT_DEFINE_COUNT 11
#define TEST_IMPLICIT_DEFINE_COUNT 12
#define TEST_GEOMETRY_DEFINE_COUNT 4

View File

@ -22,14 +22,16 @@ code = '''
#define STRINGIZE_(x) #x
#include STRINGIZE(LFSP)
#else
#define LFSP_VERSION LFS_VERSION
#define LFSP_VERSION_MAJOR LFS_VERSION_MAJOR
#define LFSP_VERSION_MINOR LFS_VERSION_MINOR
#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
@ -58,7 +60,10 @@ code = '''
# test we can mount in a new version
[cases.test_compat_forward_mount]
if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR'
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
@ -74,13 +79,22 @@ code = '''
// 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_VERSION_MAJOR == LFSP_VERSION_MAJOR'
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
@ -102,6 +116,11 @@ code = '''
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;
@ -132,7 +151,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR'
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
@ -166,6 +188,11 @@ code = '''
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;
@ -214,7 +241,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR'
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
@ -251,6 +281,11 @@ code = '''
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;
@ -321,7 +356,10 @@ code = '''
# test we can write dirs in a new version
[cases.test_compat_forward_write_dirs]
defines.COUNT = 10
if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR'
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
@ -343,6 +381,11 @@ code = '''
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];
@ -380,7 +423,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR'
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
@ -420,6 +466,11 @@ code = '''
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++) {
@ -494,7 +545,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR'
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
@ -537,6 +591,11 @@ code = '''
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++) {
@ -636,7 +695,10 @@ code = '''
# test we can mount in an old version
[cases.test_compat_backward_mount]
if = 'LFS_VERSION == LFSP_VERSION'
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
@ -651,13 +713,17 @@ code = '''
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_VERSION == LFSP_VERSION'
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
@ -709,7 +775,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = 'LFS_VERSION == LFSP_VERSION'
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
@ -791,7 +860,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = 'LFS_VERSION == LFSP_VERSION'
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
@ -898,7 +970,10 @@ code = '''
# test we can write dirs in an old version
[cases.test_compat_backward_write_dirs]
defines.COUNT = 10
if = 'LFS_VERSION == LFSP_VERSION'
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
@ -957,7 +1032,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = 'LFS_VERSION == LFSP_VERSION'
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
lfs_t lfs;
@ -1071,7 +1149,10 @@ code = '''
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = 'LFS_VERSION == LFSP_VERSION'
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
lfs_t lfs;
@ -1280,7 +1361,10 @@ code = '''
# test that we correctly bump the minor version
[cases.test_compat_minor_bump]
in = 'lfs.c'
if = 'LFS_DISK_VERSION_MINOR > 0'
if = '''
LFS_DISK_VERSION_MINOR > 0
&& DISK_VERSION == 0
'''
code = '''
// create a superblock
lfs_t lfs;
@ -1316,45 +1400,54 @@ code = '''
// 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 have changed
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_get(&lfs, &mdir, LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock)
=> LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock));
lfs_superblock_fromle32(&superblock);
assert((superblock.version >> 16) & 0xffff == LFS_DISK_VERSION_MAJOR);
assert((superblock.version >> 0) & 0xffff == LFS_DISK_VERSION_MINOR);
// 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;
// minor version should have changed
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_get(&lfs, &mdir, LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock)
=> LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock));
lfs_superblock_fromle32(&superblock);
assert((superblock.version >> 16) & 0xffff == LFS_DISK_VERSION_MAJOR);
assert((superblock.version >> 0) & 0xffff == LFS_DISK_VERSION_MINOR);
// yep, still changed
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
lfs_unmount(&lfs) => 0;
'''

View File

@ -90,7 +90,10 @@ code = '''
# partial prog, may not be byte in order!
[cases.test_powerloss_partial_prog]
if = "PROG_SIZE < BLOCK_SIZE"
if = '''
PROG_SIZE < BLOCK_SIZE
&& (DISK_VERSION == 0 || DISK_VERSION >= 0x00020001)
'''
defines.BYTE_OFF = ["0", "PROG_SIZE-1", "PROG_SIZE/2"]
defines.BYTE_VALUE = [0x33, 0xcc]
in = "lfs.c"

View File

@ -34,6 +34,54 @@ code = '''
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
'''
# test we can read superblock info through lfs_fs_stat
[cases.test_superblocks_stat]
if = 'DISK_VERSION == 0'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// test we can mount and read fsinfo
lfs_mount(&lfs, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
assert(fsinfo.name_max == LFS_NAME_MAX);
assert(fsinfo.file_max == LFS_FILE_MAX);
assert(fsinfo.attr_max == LFS_ATTR_MAX);
lfs_unmount(&lfs) => 0;
'''
[cases.test_superblocks_stat_tweaked]
if = 'DISK_VERSION == 0'
defines.TWEAKED_NAME_MAX = 63
defines.TWEAKED_FILE_MAX = '(1 << 16)-1'
defines.TWEAKED_ATTR_MAX = 512
code = '''
// create filesystem with tweaked params
struct lfs_config tweaked_cfg = *cfg;
tweaked_cfg.name_max = TWEAKED_NAME_MAX;
tweaked_cfg.file_max = TWEAKED_FILE_MAX;
tweaked_cfg.attr_max = TWEAKED_ATTR_MAX;
lfs_t lfs;
lfs_format(&lfs, &tweaked_cfg) => 0;
// test we can mount and read these params with the original config
lfs_mount(&lfs, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
assert(fsinfo.name_max == TWEAKED_NAME_MAX);
assert(fsinfo.file_max == TWEAKED_FILE_MAX);
assert(fsinfo.attr_max == TWEAKED_ATTR_MAX);
lfs_unmount(&lfs) => 0;
'''
# expanding superblock
[cases.test_superblocks_expand]
defines.BLOCK_CYCLES = [32, 33, 1]