Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Haster <geky@geky.net>2023-06-30 20:33:10 +0300
committerGitHub <noreply@github.com>2023-06-30 20:33:10 +0300
commit611c9b20db2b99faee261daa7cc9bbe175d3eaca (patch)
tree77d3d056a119f3a40defff8fe04a7acf49a67788
parent66f07563c333a6cfe25e51633ded6851568a0d49 (diff)
parenta942cdba664dd9f7b48dd7494807c29ef6fe313c (diff)
Merge pull request #848 from littlefs-project/develv2.7.0
Minor release: v2.7
-rw-r--r--.github/workflows/release.yml2
-rw-r--r--.github/workflows/test.yml62
-rw-r--r--README.md5
-rw-r--r--lfs.c202
-rw-r--r--lfs.h31
-rw-r--r--runners/test_runner.c15
-rw-r--r--runners/test_runner.h7
-rw-r--r--tests/test_compat.toml165
-rw-r--r--tests/test_powerloss.toml5
-rw-r--r--tests/test_superblocks.toml48
10 files changed, 451 insertions, 91 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b2ead2e..c0d446d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -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}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2cee352..ccb08fe 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -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}
diff --git a/README.md b/README.md
index 3afddfd..df7ee00 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/lfs.c b/lfs.c
index f0daf54..38b825d 100644
--- a/lfs.c
+++ b/lfs.c
@@ -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;
+
+ } 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);
+ // 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) {
diff --git a/lfs.h b/lfs.h
index eb5c355..1081735 100644
--- a/lfs.h
+++ b/lfs.h
@@ -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
diff --git a/runners/test_runner.c b/runners/test_runner.c
index abc867c..27f8524 100644
--- a/runners/test_runner.c
+++ b/runners/test_runner.c
@@ -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 = {
diff --git a/runners/test_runner.h b/runners/test_runner.h
index 9ff1f79..e30d492 100644
--- a/runners/test_runner.h
+++ b/runners/test_runner.h
@@ -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
diff --git a/tests/test_compat.toml b/tests/test_compat.toml
index a36c38a..ba44714 100644
--- a/tests/test_compat.toml
+++ b/tests/test_compat.toml
@@ -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;
'''
diff --git a/tests/test_powerloss.toml b/tests/test_powerloss.toml
index 06f8661..92c323b 100644
--- a/tests/test_powerloss.toml
+++ b/tests/test_powerloss.toml
@@ -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"
diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml
index 689bbcd..0aff84b 100644
--- a/tests/test_superblocks.toml
+++ b/tests/test_superblocks.toml
@@ -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]