From 85abdf1c9a64600794b63bd8d95f524d69d6c38b Mon Sep 17 00:00:00 2001 From: geky-bot Date: Fri, 30 Jun 2023 18:37:28 +0000 Subject: [PATCH 01/12] Squashed 'littlefs/' changes from 66f0756..611c9b2 611c9b2 Merge pull request #848 from littlefs-project/devel a942cdb Bumped minor version to v2.7 225fc31 Merge pull request #846 from littlefs-project/link-chan-fatfs 5db368c Merge pull request #839 from littlefs-project/configurable-disk-version f09c6a4 Merge pull request #838 from littlefs-project/fs-stat 79cc75d Added LFS_MULTIVERSION and testing of lfs2.0 to CI eb9af7a Added LFS_MULTIVERSION, made lfs2.0 support a compile-time option b72c96d Added support for writing on-disk version lfs2.0 265692e Removed fsinfo.block_usage for now 08a132e Added a link to ChaN's FatFS implementation c5fb3f1 Changed fsinfo.minor_version -> fsinfo.disk_version 8610f7c Increased context on failures for Valgrind in CI a51be18 Removed previous-version lfsp_fs_stat checks in test_compat a7ccc1d Promoted lfs_gstate_needssuperblock to be available in readonly builds fdee127 Removed use of LFS_VERSION in test_compat 87bbf1d Added lfs_fs_stat for access to filesystem status/configuration git-subtree-dir: littlefs git-subtree-split: 611c9b20db2b99faee261daa7cc9bbe175d3eaca --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 62 ++++++++++- README.md | 5 + lfs.c | 202 ++++++++++++++++++++++++++-------- lfs.h | 31 +++++- runners/test_runner.c | 15 +++ runners/test_runner.h | 7 +- tests/test_compat.toml | 165 +++++++++++++++++++++------ tests/test_powerloss.toml | 5 +- tests/test_superblocks.toml | 48 ++++++++ 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] From 96163855a518c95692bf792efbb73c27af653b48 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 3 Jul 2023 11:07:48 -0500 Subject: [PATCH 02/12] Update copyright notice --- LICENSE.md | 1 + lfs_fuse.c | 1 + lfs_fuse_bd.c | 1 + lfs_fuse_bd.h | 1 + 4 files changed, 4 insertions(+) diff --git a/LICENSE.md b/LICENSE.md index ed69bea..3c97553 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,3 +1,4 @@ +Copyright (c) 2022, the littlefs authors. Copyright (c) 2017, Arm Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/lfs_fuse.c b/lfs_fuse.c index 3c87dad..3fcb51b 100644 --- a/lfs_fuse.c +++ b/lfs_fuse.c @@ -1,6 +1,7 @@ /* * FUSE wrapper for the littlefs * + * Copyright (c) 2022, the littlefs authors. * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/lfs_fuse_bd.c b/lfs_fuse_bd.c index 9859c32..952825b 100644 --- a/lfs_fuse_bd.c +++ b/lfs_fuse_bd.c @@ -1,6 +1,7 @@ /* * Linux user-space block device wrapper * + * Copyright (c) 2022, the littlefs authors. * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/lfs_fuse_bd.h b/lfs_fuse_bd.h index 07197b2..5465c84 100644 --- a/lfs_fuse_bd.h +++ b/lfs_fuse_bd.h @@ -1,6 +1,7 @@ /* * Linux user-space block device wrapper * + * Copyright (c) 2022, the littlefs authors. * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ From 3e6041a59977a6652504d22798eb42f52859aeb9 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 3 Jul 2023 11:22:49 -0500 Subject: [PATCH 03/12] Added a littlefs-fuse specific version number Previously this was fixed to the underlying littlefs core version, but that state of things doesn't work if you ever want new features in littelfs-fuse outside of littlefs minor releases. This currently matches the underlying core minor version, to tie things together, but will fall out of sync as things are updated. --- lfs_fuse.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lfs_fuse.c b/lfs_fuse.c index 3c87dad..696e794 100644 --- a/lfs_fuse.c +++ b/lfs_fuse.c @@ -25,6 +25,18 @@ #include +// littefs-fuse version +// +// Note this is different from the littlefs core version, and littlefs +// on-disk version +// +// Major (top-nibble), incremented on backwards incompatible changes +// Minor (bottom-nibble), incremented on feature additions +#define LFS_FUSE_VERSION 0x00020006 +#define LFS_FUSE_VERSION_MAJOR (0xffff & (LFS_FUSE_VERSION >> 16)) +#define LFS_FUSE_VERSION_MINOR (0xffff & (LFS_FUSE_VERSION >> 0)) + + // config and other state static struct lfs_config config = {0}; static const char *device = NULL; @@ -467,10 +479,12 @@ int lfs_fuse_opt_proc(void *data, const char *arg, exit(1); case KEY_VERSION: + fprintf(stderr, "littlefs-fuse version: v%d.%d\n", + LFS_FUSE_VERSION_MAJOR, LFS_FUSE_VERSION_MINOR); fprintf(stderr, "littlefs version: v%d.%d\n", - LFS_VERSION_MAJOR, LFS_VERSION_MINOR); + LFS_VERSION_MAJOR, LFS_VERSION_MINOR); fprintf(stderr, "littlefs disk version: v%d.%d\n", - LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR); + LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR); fuse_opt_add_arg(args, "--version"); fuse_main(args->argc, args->argv, &lfs_fuse_ops, NULL); exit(0); From 6096245af895cf3693fec163a6dfe8dbfe71e640 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 3 Jul 2023 11:34:00 -0500 Subject: [PATCH 04/12] Added GitHub Action to test littlefs-fuse, removed .travis.yml Based on GitHub Action script in the core littlefs repo. --- .github/workflows/test.yml | 50 +++++++++++++++++++++ .travis.yml | 90 -------------------------------------- 2 files changed, 50 insertions(+), 90 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..95b9251 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,50 @@ +name: test +on: [push, pull_request] + +defaults: + run: + shell: bash -euv -o pipefail {0} + +env: + CFLAGS: -Werror + MAKEFLAGS: -j + +jobs: + # self-host test with littlefs-fuse + test: + 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 libfuse-dev + sudo pip3 install toml + gcc --version + python3 --version + fusermount -V + - name: setup + run: | + # setup disk for littlefs-fuse + mkdir mount + LOOP=$(sudo losetup -f) + sudo chmod a+rw $LOOP + dd if=/dev/zero bs=512 count=128K of=disk + losetup $LOOP disk + echo "LOOP=$LOOP" >> $GITHUB_ENV + - name: test + run: | + # self-host test + make + + ./lfs --format $LOOP + ./lfs $LOOP mount + + ls mount + cp -r littlefs mount/littlefs + cd mount/littlefs + stat . + ls -flh + make -B test-runner + make -B test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 93c107e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,90 +0,0 @@ -# Environment variables -env: - global: - - CFLAGS=-Werror - -cache: - pip: true - directories: - - $HOME/.cache/apt - -# CI Jobs -jobs: - include: - # Test stage - - stage: test - env: - - STAGE=test - install: - - sudo apt-get install python3 python3-pip - - sudo pip3 install toml - - sudo apt-get install libfuse-dev - - fusermount -V - - gcc --version - before_script: - - mkdir mount - - sudo chmod a+rw /dev/loop0 - - dd if=/dev/zero bs=512 count=128K of=disk - - losetup /dev/loop0 disk - script: - # Build - - make - # Format and mount - - ./lfs --format /dev/loop0 - - ./lfs /dev/loop0 mount - # Some simple operations than self hosting test - - ls mount - - cp -r littlefs mount/littlefs - - cd mount/littlefs - - stat . - - ls -flh - - make -B test - - # Deploy stage for updating versions and tags - - stage: deploy - env: - - STAGE=deploy - script: - - | - bash << 'SCRIPT' - set -ev - # Find version defined in lfs.h - LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' littlefs/lfs.h | cut -d ' ' -f3) - LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16))) - LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >> 0))) - # Grab latest patch from repo tags, default to 0, needs finagling - # to get past GitHub's pagination API - PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR. - PREV_URL=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" -I \ - | sed -n '/^Link/{s/.*<\(.*\)>; rel="last"/\1/;p;q0};$q1' \ - || echo $PREV_URL) - LFS_VERSION_PATCH=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" \ - | jq 'map(.ref | match("\\bv.*\\..*\\.(.*)$";"g") - .captures[].string | tonumber) | max + 1' \ - || echo 0) - # We have our new version - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH" - echo "VERSION $LFS_VERSION" - # Check that we're the most recent commit - CURRENT_COMMIT=$(curl -f -u "$GEKY_BOT_RELEASES" \ - https://api.github.com/repos/$TRAVIS_REPO_SLUG/commits/master \ - | jq -re '.sha') - [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] || exit 0 - # Create major branch (vN) - git branch v$LFS_VERSION_MAJOR HEAD - git push https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \ - v$LFS_VERSION_MAJOR - # Create patch version tag (vN.N.N) - curl -f -u "$GEKY_BOT_RELEASES" -X POST \ - https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \ - -d "{ - \"ref\": \"refs/tags/$LFS_VERSION\", - \"sha\": \"$TRAVIS_COMMIT\" - }" - SCRIPT - -# Job control -stages: - - name: test - - name: deploy - if: branch = master AND type = push From f4606307d47868eb7d2a136cc44fb504f155e73a Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 3 Jul 2023 11:56:29 -0500 Subject: [PATCH 05/12] Added GitHub Action release script On passing tests on master: 1. Update "vN" branch 2. Create release notes containing all non-merge commits 3. If patch release, publish release 4. If >=minor release, create draft release but let a human publish This is based on the current release script in the core littlefs repo. --- .github/workflows/release.yml | 109 ++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a49c75c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,109 @@ +name: release +on: + workflow_run: + workflows: [test] + branches: [master] + types: [completed] + +defaults: + run: + shell: bash -euv -o pipefail {0} + +jobs: + release: + runs-on: ubuntu-22.04 + + # need to manually check for a couple things + # - tests passed? + # - we are the most recent commit on master? + if: ${{github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_sha == github.sha}} + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{github.event.workflow_run.head_sha}} + # need workflow access since we push branches + # containing workflows + token: ${{secrets.BOT_TOKEN}} + # need all tags + fetch-depth: 0 + + - name: find-version + run: | + # rip version from lfs_fuse.c + LFS_FUSE_VERSION="$( \ + grep -o '^#define LFS_FUSE_VERSION .*$' lfs_fuse.c \ + | awk '{print $3}')" + LFS_FUSE_VERSION_MAJOR="$((0xffff & ($LFS_FUSE_VERSION >> 16)))" + LFS_FUSE_VERSION_MINOR="$((0xffff & ($LFS_FUSE_VERSION >> 0)))" + + # find a new patch version based on what we find in our tags + LFS_FUSE_VERSION_PATCH="$( \ + ( git describe --tags --abbrev=0 \ + --match="v$LFS_FUSE_VERSION_MAJOR.$LFS_FUSE_VERSION_MINOR.*" \ + || echo 'v0.0.-1' ) \ + | awk -F '.' '{print $3+1}')" + + # found new version + LFS_FUSE_VERSION="v$LFS_FUSE_VERSION_MAJOR` + `.$LFS_FUSE_VERSION_MINOR` + `.$LFS_FUSE_VERSION_PATCH" + echo "LFS_FUSE_VERSION=$LFS_FUSE_VERSION" + echo "LFS_FUSE_VERSION=$LFS_FUSE_VERSION" >> $GITHUB_ENV + echo "LFS_FUSE_VERSION_MAJOR=$LFS_FUSE_VERSION_MAJOR" >> $GITHUB_ENV + echo "LFS_FUSE_VERSION_MINOR=$LFS_FUSE_VERSION_MINOR" >> $GITHUB_ENV + echo "LFS_FUSE_VERSION_PATCH=$LFS_FUSE_VERSION_PATCH" >> $GITHUB_ENV + + # try to find previous version? + - name: find-prev-version + continue-on-error: true + run: | + LFS_FUSE_PREV_VERSION="$( \ + git describe --tags --abbrev=0 --match 'v*' \ + || true)" + echo "LFS_FUSE_PREV_VERSION=$LFS_FUSE_PREV_VERSION" + echo "LFS_FUSE_PREV_VERSION=$LFS_FUSE_PREV_VERSION" >> $GITHUB_ENV + + # find changes from history + - name: create-changes + run: | + [ -n "$LFS_FUSE_PREV_VERSION" ] || exit 0 + # use explicit link to github commit so that release notes can + # be copied elsewhere + git log "$LFS_FUSE_PREV_VERSION.." \ + --grep='^Merge' --invert-grep \ + --format="format:[\`%h\`](` + `https://github.com/$GITHUB_REPOSITORY/commit/%h) %s" \ + > changes.txt + echo "CHANGES:" + cat changes.txt + + # create and update major branches (vN) + - name: create-major-branches + run: | + # create major branch + git branch "v$LFS_FUSE_VERSION_MAJOR" HEAD + + # push! + git push --atomic origin "v$LFS_FUSE_VERSION_MAJOR" + + # build release notes + - name: create-release + run: | + # create release and patch version tag (vN.N.N) + # only draft if not a patch release + touch release.txt + [ -e changes.txt ] && cat changes.txt >> release.txt + cat release.txt + + curl -sS -X POST -H "authorization: token ${{secrets.BOT_TOKEN}}" \ + "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases" \ + -d "$(jq -n --rawfile release release.txt '{ + tag_name: env.LFS_FUSE_VERSION, + name: env.LFS_FUSE_VERSION | rtrimstr(".0"), + target_commitish: "${{github.event.workflow_run.head_sha}}", + draft: env.LFS_FUSE_VERSION | endswith(".0"), + body: $release, + }' | tee /dev/stderr)" + From 1a7881cd5938a0806511aa50b49b2155464ab373 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 3 Jul 2023 15:25:06 -0500 Subject: [PATCH 06/12] Added some on-disk minor version features - Integrated lfs_fs_stat into statvfs, so statvfs.namemax reports the on-disk name_max correctly. - Added ./lfs --stat, which allows you to access littlefs-specific info without needing to mount the filesystem. Currently this includes: - on-disk version - block_size (from ioctl) - block_count (from ioctl) - block usage as both used, free, and percentage, for convenience - on-disk name_max - on-disk file_max - on-disk attr_max This may be extended in the future Example output: $ ./lfs --stat /dev/loop9 disk_version: lfs2.1 block_size: 512 block_count: 131072 used: 2/131072 (0.0%) free: 131070/131072 (100.0%) name_max: 255 file_max: 2147483647 attr_max: 1022 - Added support for writing previous minor versions of littlefs via the --disk_version (-d) flag. Note because of limitations in FUSE's argument parser, this requires an equal sign: $ ./lfs -d=lfs2.0 --format /dev/loop9 $ ./lfs --stat /dev/loop9 disk_version: lfs2.0 ... --- Makefile | 3 ++ lfs_fuse.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ca73029..c6725a1 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,9 @@ override CFLAGS += -I. -Ilittlefs override CFLAGS += -std=c99 -Wall -pedantic override CFLAGS += -D_FILE_OFFSET_BITS=64 override CFLAGS += -D_XOPEN_SOURCE=700 +# enable multiversion support in littlefs +override CFLAGS += -DLFS_MULTIVERSION +# enable migrate support in littlefs override CFLAGS += -DLFS_MIGRATE override LFLAGS += -lfuse diff --git a/lfs_fuse.c b/lfs_fuse.c index 696e794..675d701 100644 --- a/lfs_fuse.c +++ b/lfs_fuse.c @@ -40,6 +40,7 @@ // config and other state static struct lfs_config config = {0}; static const char *device = NULL; +static bool stat_ = false; static bool format = false; static bool migrate = false; static lfs_t lfs; @@ -82,6 +83,59 @@ void *lfs_fuse_init(struct fuse_conn_info *conn) { return 0; } +int lfs_fuse_stat(void) { + int err = lfs_fuse_bd_create(&config, device); + if (err) { + return err; + } + + lfs_fuse_defaults(&config); + + err = lfs_mount(&lfs, &config); + if (err) { + goto failed; + } + + // get on-disk info + struct lfs_fsinfo fsinfo; + err = lfs_fs_stat(&lfs, &fsinfo); + if (err) { + goto failed; + } + + // get block usage + lfs_ssize_t in_use = lfs_fs_size(&lfs); + if (in_use < 0) { + err = in_use; + goto failed; + } + + // print to stdout + printf("disk_version: lfs%d.%d\n", + 0xffff & (fsinfo.disk_version >> 16), + 0xffff & (fsinfo.disk_version >> 0)); + printf("block_size: %d\n", config.block_size); + printf("block_count: %d\n", config.block_count); + printf(" used: %d/%d (%.1f%%)\n", + in_use, + config.block_count, + 100.0f * (float)in_use / (float)config.block_count); + printf(" free: %d/%d (%.1f%%)\n", + config.block_count-in_use, + config.block_count, + 100.0f * (float)(config.block_count-in_use) + / (float)config.block_count); + printf("name_max: %d\n", fsinfo.name_max); + printf("file_max: %d\n", fsinfo.file_max); + printf("attr_max: %d\n", fsinfo.attr_max); + + err = lfs_unmount(&lfs); + +failed: + lfs_fuse_bd_destroy(&config); + return err; +} + int lfs_fuse_format(void) { int err = lfs_fuse_bd_create(&config, device); if (err) { @@ -129,6 +183,14 @@ void lfs_fuse_destroy(void *eh) { int lfs_fuse_statfs(const char *path, struct statvfs *s) { memset(s, 0, sizeof(struct statvfs)); + // get the on-disk name_max from littlefs + struct lfs_fsinfo fsinfo; + int err = lfs_fs_stat(&lfs, &fsinfo); + if (err) { + return err; + } + + // get the filesystem block usage from littlefs lfs_ssize_t in_use = lfs_fs_size(&lfs); if (in_use < 0) { return in_use; @@ -139,7 +201,7 @@ int lfs_fuse_statfs(const char *path, struct statvfs *s) { s->f_blocks = config.block_count; s->f_bfree = config.block_count - in_use; s->f_bavail = config.block_count - in_use; - s->f_namemax = config.name_max; + s->f_namemax = fsinfo.name_max; return 0; } @@ -403,14 +465,19 @@ static struct fuse_operations lfs_fuse_ops = { enum lfs_fuse_keys { KEY_HELP, KEY_VERSION, + KEY_STAT, KEY_FORMAT, KEY_MIGRATE, + KEY_DISK_VERSION, }; #define OPT(t, p) { t, offsetof(struct lfs_config, p), 0} static struct fuse_opt lfs_fuse_opts[] = { + FUSE_OPT_KEY("--stat", KEY_STAT), FUSE_OPT_KEY("--format", KEY_FORMAT), FUSE_OPT_KEY("--migrate", KEY_MIGRATE), + {"-d=", -1U, KEY_DISK_VERSION}, + {"--disk_version=", -1U, KEY_DISK_VERSION}, OPT("-b=%" SCNu32, block_size), OPT("--block_size=%" SCNu32, block_size), OPT("--block_count=%" SCNu32, block_count), @@ -438,8 +505,10 @@ static const char help_text[] = " -V --version print version\n" "\n" "littlefs options:\n" +" --stat print filesystem info instead of mounting\n" " --format format instead of mounting\n" " --migrate migrate previous version instead of mounting\n" +" -d --disk_version attempt to use this on-disk version of littlefs\n" " -b --block_size logical block size, overrides the block device\n" " --block_count block count, overrides the block device\n" " --block_cycles number of erase cycles before eviction (512)\n" @@ -464,6 +533,10 @@ int lfs_fuse_opt_proc(void *data, const char *arg, } break; + case KEY_STAT: + stat_ = true; + return 0; + case KEY_FORMAT: format = true; return 0; @@ -483,11 +556,72 @@ int lfs_fuse_opt_proc(void *data, const char *arg, LFS_FUSE_VERSION_MAJOR, LFS_FUSE_VERSION_MINOR); fprintf(stderr, "littlefs version: v%d.%d\n", LFS_VERSION_MAJOR, LFS_VERSION_MINOR); - fprintf(stderr, "littlefs disk version: v%d.%d\n", + fprintf(stderr, "littlefs disk version: lfs%d.%d\n", LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR); fuse_opt_add_arg(args, "--version"); fuse_main(args->argc, args->argv, &lfs_fuse_ops, NULL); exit(0); + + case KEY_DISK_VERSION: { + // skip opt prefix + const char *arg_ = strchr(arg, '='); + if (arg_) { + arg = arg_ + 1; + } + + // parse out the requested disk version + // supported formats: + // - no-prefix - 2.1 + // - v-prefix - v2.1 + // - lfs-prefix - lfs2.1 + // - littlefs-prefix - littlefs2.1 + const char *orig_arg = arg; + if (strlen(arg) >= strlen("v") + && memcmp(arg, "v", strlen("v")) == 0) { + arg += strlen("v"); + } else if (strlen(arg) >= strlen("lfs") + && memcmp(arg, "lfs", strlen("lfs")) == 0) { + arg += strlen("lfs"); + } else if (strlen(arg) >= strlen("littlefs") + && memcmp(arg, "littlefs", strlen("littlefs")) == 0) { + arg += strlen("littlefs"); + } + + char *parsed; + uintmax_t major = strtoumax(arg, &parsed, 0); + if (parsed == arg) { + goto invalid_version; + } + arg = parsed; + + if (arg[0] != '.') { + goto invalid_version; + } + arg += 1; + + uintmax_t minor = strtoumax(arg, &parsed, 0); + if (parsed == arg) { + goto invalid_version; + } + arg = parsed; + + if (arg[0] != '\0') { + goto invalid_version; + } + + if (major > 0xffff || minor > 0xffff) { + goto invalid_version; + } + + config.disk_version + = ((major & 0xffff) << 16) + | ((minor & 0xffff) << 0); + return 0; + + invalid_version: + fprintf(stderr, "invalid disk version: \"%s\"\n", orig_arg); + exit(1); + } } return 1; @@ -502,6 +636,16 @@ int main(int argc, char *argv[]) { exit(1); } + if (stat_) { + // stat time, no mount + int err = lfs_fuse_stat(); + if (err) { + LFS_ERROR("%s", strerror(-err)); + exit(-err); + } + exit(0); + } + if (format) { // format time, no mount int err = lfs_fuse_format(); From ec4c8857579243c8ab1423d2947c338f782994a4 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 21 Aug 2023 13:32:03 -0500 Subject: [PATCH 07/12] Added CI testing of minor on-disk features: --stat and -d=lfs2.0 --- .github/workflows/test.yml | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 95b9251..bf1cd95 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,6 +39,7 @@ jobs: make ./lfs --format $LOOP + ./lfs --stat $LOOP ./lfs $LOOP mount ls mount @@ -48,3 +49,61 @@ jobs: ls -flh make -B test-runner make -B test + + # test older versions + 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 libfuse-dev + sudo pip3 install toml + gcc --version + python3 --version + fusermount -V + - name: setup + run: | + # setup disk for littlefs-fuse + mkdir mount + LOOP=$(sudo losetup -f) + sudo chmod a+rw $LOOP + dd if=/dev/zero bs=512 count=128K of=disk + losetup $LOOP disk + echo "LOOP=$LOOP" >> $GITHUB_ENV + - name: test + run: | + # self-host test + make + + ./lfs -d=lfs2.0 --format $LOOP + ./lfs --stat $LOOP + ./lfs -d=lfs2.0 $LOOP mount + + ls mount + cp -r littlefs mount/littlefs + cd mount/littlefs + stat . + ls -flh + make -B test-runner + make -B test + - name: test-migrate + run: | + # self-host test, this time migrating to current version + make + + ./lfs -d=lfs2.0 --format $LOOP + ./lfs --stat $LOOP + ./lfs $LOOP mount + + ls mount + cp -r littlefs mount/littlefs + cd mount/littlefs + stat . + ls -flh + make -B test-runner + make -B test + + ./lfs --stat $LOOP From 4aa80f5db793521e0065d30e6ad93e6a4bf23ea8 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 21 Aug 2023 13:42:50 -0500 Subject: [PATCH 08/12] Made CI unmount littlefs at the end of each test Probably should have been doing this before. This leaves CI in a stable unmounted state between CI steps, which is necessary for the lfs2.0-migrate test to work. --- .github/workflows/test.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf1cd95..3931a48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,6 +50,10 @@ jobs: make -B test-runner make -B test + cd ../.. + umount mount + ./lfs --stat $LOOP + # test older versions test-lfs2_0: runs-on: ubuntu-22.04 @@ -89,6 +93,10 @@ jobs: ls -flh make -B test-runner make -B test + + cd ../.. + umount mount + ./lfs --stat $LOOP - name: test-migrate run: | # self-host test, this time migrating to current version @@ -106,4 +114,6 @@ jobs: make -B test-runner make -B test + cd ../.. + umount mount ./lfs --stat $LOOP From e06caf4e9ba0aa0d0323a91acee6b8972a393bad Mon Sep 17 00:00:00 2001 From: taorye Date: Mon, 14 Nov 2022 14:02:11 +0800 Subject: [PATCH 09/12] fix: correct block_count with block_size BLKGETSIZE means `return device size /512 (long *arg)` --- lfs_fuse_bd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfs_fuse_bd.c b/lfs_fuse_bd.c index 9859c32..217ef6e 100644 --- a/lfs_fuse_bd.c +++ b/lfs_fuse_bd.c @@ -46,7 +46,7 @@ int lfs_fuse_bd_create(struct lfs_config *cfg, const char *path) { if (err) { return -errno; } - cfg->block_count = size; + cfg->block_count = size * 512 / cfg->block_size; } // setup function pointers From f448139bdcd9a0f564fe69df28cb74f354825dcb Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 21 Aug 2023 13:37:41 -0500 Subject: [PATCH 10/12] Swapped BLKGETSIZE for BLKGETSIZE64 BLKGETSIZE64 seems to report the size in bytes, which is what we actually want. BLKGETSIZE reported the size in "512-byte blocks", which just complicates our final block_count calculation. --- lfs_fuse_bd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lfs_fuse_bd.c b/lfs_fuse_bd.c index 217ef6e..1422ef1 100644 --- a/lfs_fuse_bd.c +++ b/lfs_fuse_bd.c @@ -41,12 +41,12 @@ int lfs_fuse_bd_create(struct lfs_config *cfg, const char *path) { // get size in sectors if (!cfg->block_count) { - long size; - int err = ioctl(fd, BLKGETSIZE, &size); + uint64_t size; + int err = ioctl(fd, BLKGETSIZE64, &size); if (err) { return -errno; } - cfg->block_count = size * 512 / cfg->block_size; + cfg->block_count = size / cfg->block_size; } // setup function pointers From d5d0a6c6ff629226af249cc1e8b941f670395ed3 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 21 Aug 2023 23:18:28 -0500 Subject: [PATCH 11/12] Fixed read/write syscalls to continue reading if result is less than expected Found by lucckb --- lfs_fuse_bd.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lfs_fuse_bd.c b/lfs_fuse_bd.c index 9859c32..ca5a186 100644 --- a/lfs_fuse_bd.c +++ b/lfs_fuse_bd.c @@ -66,6 +66,7 @@ void lfs_fuse_bd_destroy(const struct lfs_config *cfg) { int lfs_fuse_bd_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { int fd = (intptr_t)cfg->context; + uint8_t *buffer_ = buffer; // check if read is valid assert(block < cfg->block_count); @@ -77,9 +78,14 @@ int lfs_fuse_bd_read(const struct lfs_config *cfg, lfs_block_t block, } // read block - ssize_t res = read(fd, buffer, (size_t)size); - if (res < 0) { - return -errno; + while (size > 0) { + ssize_t res = read(fd, buffer_, (size_t)size); + if (res < 0) { + return -errno; + } + + buffer_ += res; + size -= res; } return 0; @@ -88,6 +94,7 @@ int lfs_fuse_bd_read(const struct lfs_config *cfg, lfs_block_t block, int lfs_fuse_bd_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { int fd = (intptr_t)cfg->context; + const uint8_t *buffer_ = buffer; // check if write is valid assert(block < cfg->block_count); @@ -99,9 +106,14 @@ int lfs_fuse_bd_prog(const struct lfs_config *cfg, lfs_block_t block, } // write block - ssize_t res = write(fd, buffer, (size_t)size); - if (res < 0) { - return -errno; + while (size > 0) { + ssize_t res = write(fd, buffer_, (size_t)size); + if (res < 0) { + return -errno; + } + + buffer_ += res; + size -= res; } return 0; From 9ac0820023768c0b49702e06964e2f6765c04cea Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 23 Aug 2023 00:13:08 -0500 Subject: [PATCH 12/12] Bumped minor version to v2.7 --- lfs_fuse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfs_fuse.c b/lfs_fuse.c index 3bf101a..7aceb2b 100644 --- a/lfs_fuse.c +++ b/lfs_fuse.c @@ -33,7 +33,7 @@ // // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS_FUSE_VERSION 0x00020006 +#define LFS_FUSE_VERSION 0x00020007 #define LFS_FUSE_VERSION_MAJOR (0xffff & (LFS_FUSE_VERSION >> 16)) #define LFS_FUSE_VERSION_MINOR (0xffff & (LFS_FUSE_VERSION >> 0))