From a01be933b615a08e1ffdd9e67642fbc76e68f99b Mon Sep 17 00:00:00 2001 From: geky-bot Date: Tue, 23 Jan 2024 19:00:24 +0000 Subject: [PATCH] Squashed 'littlefs/' changes from 3513ff1..f53a0cc f53a0cc Merge pull request #929 from littlefs-project/devel 42910bc Bumped minor version to v2.9 a3e1d12 Merge pull request #915 from littlefs-project/well-done a70870c Renamed internal functions _raw* -> _*_ ceb17a0 Merge pull request #917 from tomscii/fix_return_value_of_lfs_rename a8a0905 Merge pull request #916 from littlefs-project/ci-ubuntu-latest 13d7861 Merge pull request #914 from littlefs-project/inline-max 8b8fd14 Added inline_max, to optionally limit the size of inlined files 09972a1 Merge pull request #913 from littlefs-project/gc-compactions ed7bd05 Merge pull request #912 from littlefs-project/relaxed-lookahead b5cd957 Extended lfs_fs_gc to compact metadata, compact_thresh 1195d60 Merge pull request #909 from littlefs-project/easy-util-defines 1711bde Merge pull request #886 from BrianPugh/macro-sanity-check f522ed9 Added tests over rename type errors 4f32738 Fix return value of lfs_rename() 6691718 Restricted LFS_FILE_MAX to signed 32-bits, <2^31, <=2147483647 1fefcbb Rearranged compile-time constant checks to live near lfs_init 6056767 Relaxed alignment requirements for lfs_malloc 897b571 Changed CI to just run on ubuntu-latest b1b10c0 Relaxed lookahead buffer alignment 1f9c3c0 Reworked the block allocator so the logic is hopefully simpler 7b68441 Renamed a number of internal block-allocator fields 9a620c7 Added LFS_CRC, easier override for lfs_crc a0c6c54 Added LFS_MALLOC/FREE, easier overrides for lfs_malloc/free c531a5e Replace erroneous LFS_FILE_MAX upper bound 4294967296 to 4294967295 8f9427d Add value-range checks for user-definable macros git-subtree-dir: littlefs git-subtree-split: f53a0cc961a8acac85f868b431d2f3e58e447ba3 --- .github/workflows/post-release.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/status.yml | 4 +- .github/workflows/test.yml | 24 +- bd/lfs_emubd.c | 6 +- lfs.c | 462 ++++++++++++++++++----------- lfs.h | 78 +++-- lfs_util.c | 3 + lfs_util.h | 18 +- runners/bench_runner.c | 2 + runners/bench_runner.h | 18 +- runners/test_runner.c | 10 + runners/test_runner.h | 20 +- tests/test_alloc.toml | 2 + tests/test_dirs.toml | 13 + tests/test_files.toml | 21 +- tests/test_orphans.toml | 4 +- 17 files changed, 447 insertions(+), 242 deletions(-) diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 6524124..8104a94 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -10,7 +10,7 @@ defaults: jobs: post-release: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: # trigger post-release in dependency repo, this indirection allows the # dependency repo to be updated often without affecting this repo. At diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ccc9e0..055b54a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ defaults: jobs: release: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest # need to manually check for a couple things # - tests passed? diff --git a/.github/workflows/status.yml b/.github/workflows/status.yml index 8bd3990..e6e983a 100644 --- a/.github/workflows/status.yml +++ b/.github/workflows/status.yml @@ -11,7 +11,7 @@ defaults: jobs: # forward custom statuses status: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: dawidd6/action-download-artifact@v2 continue-on-error: true @@ -60,7 +60,7 @@ jobs: # forward custom pr-comments comment: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest # only run on success (we don't want garbage comments!) if: ${{github.event.workflow_run.conclusion == 'success'}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ccb08fe..db3413b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ env: jobs: # run tests test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: @@ -329,7 +329,7 @@ jobs: # # this grows exponentially, so it doesn't turn out to be that many test-pls: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: @@ -359,7 +359,7 @@ jobs: # run with LFS_NO_INTRINSICS to make sure that works test-no-intrinsics: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install @@ -376,7 +376,7 @@ jobs: # run LFS_MULTIVERSION tests test-multiversion: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install @@ -393,7 +393,7 @@ jobs: # run tests on the older version lfs2.0 test-lfs2_0: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install @@ -412,7 +412,7 @@ jobs: # run under Valgrind to check for memory errors test-valgrind: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install @@ -434,7 +434,7 @@ jobs: # test that compilation is warning free under clang # run with Clang, mostly to check for Clang-specific warnings test-clang: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install @@ -457,7 +457,7 @@ jobs: # # note there's no real benefit to running these on multiple archs bench: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install @@ -533,7 +533,7 @@ jobs: # run compatibility tests using the current master as the previous version test-compat: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 if: ${{github.event_name == 'pull_request'}} @@ -569,7 +569,7 @@ jobs: # self-host with littlefs-fuse for a fuzz-like test fuse: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest if: ${{!endsWith(github.ref, '-prefix')}} steps: - uses: actions/checkout@v2 @@ -619,7 +619,7 @@ jobs: # test migration using littlefs-fuse migrate: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest if: ${{!endsWith(github.ref, '-prefix')}} steps: - uses: actions/checkout@v2 @@ -691,7 +691,7 @@ jobs: # status related tasks that run after tests status: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest needs: [test, bench] steps: - uses: actions/checkout@v2 diff --git a/bd/lfs_emubd.c b/bd/lfs_emubd.c index c27ae30..9d88d05 100644 --- a/bd/lfs_emubd.c +++ b/bd/lfs_emubd.c @@ -451,7 +451,7 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { /// Additional extended API for driving test features /// -static int lfs_emubd_rawcrc(const struct lfs_config *cfg, +static int lfs_emubd_crc_(const struct lfs_config *cfg, lfs_block_t block, uint32_t *crc) { lfs_emubd_t *bd = cfg->context; @@ -480,7 +480,7 @@ int lfs_emubd_crc(const struct lfs_config *cfg, lfs_block_t block, uint32_t *crc) { LFS_EMUBD_TRACE("lfs_emubd_crc(%p, %"PRIu32", %p)", (void*)cfg, block, crc); - int err = lfs_emubd_rawcrc(cfg, block, crc); + int err = lfs_emubd_crc_(cfg, block, crc); LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err); return err; } @@ -491,7 +491,7 @@ int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) { uint32_t crc_ = 0xffffffff; for (lfs_block_t i = 0; i < cfg->block_count; i++) { uint32_t i_crc; - int err = lfs_emubd_rawcrc(cfg, i, &i_crc); + int err = lfs_emubd_crc_(cfg, i, &i_crc); if (err) { LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err); return err; diff --git a/lfs.c b/lfs.c index a152687..d5885ca 100644 --- a/lfs.c +++ b/lfs.c @@ -550,9 +550,9 @@ static int lfs_dir_compact(lfs_t *lfs, lfs_mdir_t *source, uint16_t begin, uint16_t end); static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file, const void *buffer, lfs_size_t size); -static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, +static lfs_ssize_t lfs_file_write_(lfs_t *lfs, lfs_file_t *file, const void *buffer, lfs_size_t size); -static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file); +static int lfs_file_sync_(lfs_t *lfs, lfs_file_t *file); static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file); static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file); @@ -574,65 +574,72 @@ static int lfs1_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); #endif -static int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir); +static int lfs_dir_rewind_(lfs_t *lfs, lfs_dir_t *dir); static lfs_ssize_t lfs_file_flushedread(lfs_t *lfs, lfs_file_t *file, void *buffer, lfs_size_t size); -static lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file, +static lfs_ssize_t lfs_file_read_(lfs_t *lfs, lfs_file_t *file, void *buffer, lfs_size_t size); -static int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file); -static lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file); +static int lfs_file_close_(lfs_t *lfs, lfs_file_t *file); +static lfs_soff_t lfs_file_size_(lfs_t *lfs, lfs_file_t *file); -static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs); -static int lfs_fs_rawtraverse(lfs_t *lfs, +static lfs_ssize_t lfs_fs_size_(lfs_t *lfs); +static int lfs_fs_traverse_(lfs_t *lfs, int (*cb)(void *data, lfs_block_t block), void *data, bool includeorphans); static int lfs_deinit(lfs_t *lfs); -static int lfs_rawunmount(lfs_t *lfs); +static int lfs_unmount_(lfs_t *lfs); /// Block allocator /// + +// allocations should call this when all allocated blocks are committed to +// the filesystem +// +// after a checkpoint, the block allocator may realloc any untracked blocks +static void lfs_alloc_ckpoint(lfs_t *lfs) { + lfs->lookahead.ckpoint = lfs->block_count; +} + +// drop the lookahead buffer, this is done during mounting and failed +// traversals in order to avoid invalid lookahead state +static void lfs_alloc_drop(lfs_t *lfs) { + lfs->lookahead.size = 0; + lfs->lookahead.next = 0; + lfs_alloc_ckpoint(lfs); +} + #ifndef LFS_READONLY static int lfs_alloc_lookahead(void *p, lfs_block_t block) { lfs_t *lfs = (lfs_t*)p; - lfs_block_t off = ((block - lfs->free.off) + lfs_block_t off = ((block - lfs->lookahead.start) + lfs->block_count) % lfs->block_count; - if (off < lfs->free.size) { - lfs->free.buffer[off / 32] |= 1U << (off % 32); + if (off < lfs->lookahead.size) { + lfs->lookahead.buffer[off / 8] |= 1U << (off % 8); } return 0; } #endif -// indicate allocated blocks have been committed into the filesystem, this -// is to prevent blocks from being garbage collected in the middle of a -// commit operation -static void lfs_alloc_ack(lfs_t *lfs) { - lfs->free.ack = lfs->block_count; -} - -// drop the lookahead buffer, this is done during mounting and failed -// traversals in order to avoid invalid lookahead state -static void lfs_alloc_drop(lfs_t *lfs) { - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); -} - #ifndef LFS_READONLY -static int lfs_fs_rawgc(lfs_t *lfs) { - // Move free offset at the first unused block (lfs->free.i) - // lfs->free.i is equal lfs->free.size when all blocks are used - lfs->free.off = (lfs->free.off + lfs->free.i) % lfs->block_count; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack); - lfs->free.i = 0; +static int lfs_alloc_scan(lfs_t *lfs) { + // move lookahead buffer to the first unused block + // + // note we limit the lookahead buffer to at most the amount of blocks + // checkpointed, this prevents the math in lfs_alloc from underflowing + lfs->lookahead.start = (lfs->lookahead.start + lfs->lookahead.next) + % lfs->block_count; + lfs->lookahead.next = 0; + lfs->lookahead.size = lfs_min( + 8*lfs->cfg->lookahead_size, + lfs->lookahead.ckpoint); // find mask of free blocks from tree - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - int err = lfs_fs_rawtraverse(lfs, lfs_alloc_lookahead, lfs, true); + memset(lfs->lookahead.buffer, 0, lfs->cfg->lookahead_size); + int err = lfs_fs_traverse_(lfs, lfs_alloc_lookahead, lfs, true); if (err) { lfs_alloc_drop(lfs); return err; @@ -645,36 +652,49 @@ static int lfs_fs_rawgc(lfs_t *lfs) { #ifndef LFS_READONLY static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { while (true) { - while (lfs->free.i != lfs->free.size) { - lfs_block_t off = lfs->free.i; - lfs->free.i += 1; - lfs->free.ack -= 1; - - if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { + // scan our lookahead buffer for free blocks + while (lfs->lookahead.next < lfs->lookahead.size) { + if (!(lfs->lookahead.buffer[lfs->lookahead.next / 8] + & (1U << (lfs->lookahead.next % 8)))) { // found a free block - *block = (lfs->free.off + off) % lfs->block_count; - - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs->free.i != lfs->free.size && - (lfs->free.buffer[lfs->free.i / 32] - & (1U << (lfs->free.i % 32)))) { - lfs->free.i += 1; - lfs->free.ack -= 1; + *block = (lfs->lookahead.start + lfs->lookahead.next) + % lfs->block_count; + + // eagerly find next free block to maximize how many blocks + // lfs_alloc_ckpoint makes available for scanning + while (true) { + lfs->lookahead.next += 1; + lfs->lookahead.ckpoint -= 1; + + if (lfs->lookahead.next >= lfs->lookahead.size + || !(lfs->lookahead.buffer[lfs->lookahead.next / 8] + & (1U << (lfs->lookahead.next % 8)))) { + return 0; + } } - - return 0; } + + lfs->lookahead.next += 1; + lfs->lookahead.ckpoint -= 1; } - // check if we have looked at all blocks since last ack - if (lfs->free.ack == 0) { - LFS_ERROR("No more free space %"PRIu32, - lfs->free.i + lfs->free.off); + // In order to keep our block allocator from spinning forever when our + // filesystem is full, we mark points where there are no in-flight + // allocations with a checkpoint before starting a set of allocations. + // + // If we've looked at all blocks since the last checkpoint, we report + // the filesystem as out of storage. + // + if (lfs->lookahead.ckpoint <= 0) { + LFS_ERROR("No more free space 0x%"PRIx32, + (lfs->lookahead.start + lfs->lookahead.next) + % lfs->cfg->block_count); return LFS_ERR_NOSPC; } - int err = lfs_fs_rawgc(lfs); + // No blocks in our lookahead buffer, we need to scan the filesystem for + // unused blocks in the next lookahead window. + int err = lfs_alloc_scan(lfs); if(err) { return err; } @@ -2146,7 +2166,7 @@ static int lfs_dir_splittingcompact(lfs_t *lfs, lfs_mdir_t *dir, && lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { // oh no! we're writing too much to the superblock, // should we expand? - lfs_ssize_t size = lfs_fs_rawsize(lfs); + lfs_ssize_t size = lfs_fs_size_(lfs); if (size < 0) { return size; } @@ -2566,7 +2586,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, /// Top level directory operations /// #ifndef LFS_READONLY -static int lfs_rawmkdir(lfs_t *lfs, const char *path) { +static int lfs_mkdir_(lfs_t *lfs, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs_fs_forceconsistency(lfs); if (err) { @@ -2588,7 +2608,7 @@ static int lfs_rawmkdir(lfs_t *lfs, const char *path) { } // build up new directory - lfs_alloc_ack(lfs); + lfs_alloc_ckpoint(lfs); lfs_mdir_t dir; err = lfs_dir_alloc(lfs, &dir); if (err) { @@ -2662,7 +2682,7 @@ static int lfs_rawmkdir(lfs_t *lfs, const char *path) { } #endif -static int lfs_dir_rawopen(lfs_t *lfs, lfs_dir_t *dir, const char *path) { +static int lfs_dir_open_(lfs_t *lfs, lfs_dir_t *dir, const char *path) { lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL); if (tag < 0) { return tag; @@ -2706,14 +2726,14 @@ static int lfs_dir_rawopen(lfs_t *lfs, lfs_dir_t *dir, const char *path) { return 0; } -static int lfs_dir_rawclose(lfs_t *lfs, lfs_dir_t *dir) { +static int lfs_dir_close_(lfs_t *lfs, lfs_dir_t *dir) { // remove from list of mdirs lfs_mlist_remove(lfs, (struct lfs_mlist *)dir); return 0; } -static int lfs_dir_rawread(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { +static int lfs_dir_read_(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { memset(info, 0, sizeof(*info)); // special offset for '.' and '..' @@ -2758,9 +2778,9 @@ static int lfs_dir_rawread(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { return true; } -static int lfs_dir_rawseek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { +static int lfs_dir_seek_(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { // simply walk from head dir - int err = lfs_dir_rawrewind(lfs, dir); + int err = lfs_dir_rewind_(lfs, dir); if (err) { return err; } @@ -2795,12 +2815,12 @@ static int lfs_dir_rawseek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { return 0; } -static lfs_soff_t lfs_dir_rawtell(lfs_t *lfs, lfs_dir_t *dir) { +static lfs_soff_t lfs_dir_tell_(lfs_t *lfs, lfs_dir_t *dir) { (void)lfs; return dir->pos; } -static int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir) { +static int lfs_dir_rewind_(lfs_t *lfs, lfs_dir_t *dir) { // reload the head dir int err = lfs_dir_fetch(lfs, &dir->m, dir->head); if (err) { @@ -3006,7 +3026,7 @@ static int lfs_ctz_traverse(lfs_t *lfs, /// Top level file operations /// -static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file, +static int lfs_file_opencfg_(lfs_t *lfs, lfs_file_t *file, const char *path, int flags, const struct lfs_file_config *cfg) { #ifndef LFS_READONLY @@ -3168,22 +3188,22 @@ static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file, #ifndef LFS_READONLY file->flags |= LFS_F_ERRED; #endif - lfs_file_rawclose(lfs, file); + lfs_file_close_(lfs, file); return err; } #ifndef LFS_NO_MALLOC -static int lfs_file_rawopen(lfs_t *lfs, lfs_file_t *file, +static int lfs_file_open_(lfs_t *lfs, lfs_file_t *file, const char *path, int flags) { static const struct lfs_file_config defaults = {0}; - int err = lfs_file_rawopencfg(lfs, file, path, flags, &defaults); + int err = lfs_file_opencfg_(lfs, file, path, flags, &defaults); return err; } #endif -static int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file) { +static int lfs_file_close_(lfs_t *lfs, lfs_file_t *file) { #ifndef LFS_READONLY - int err = lfs_file_rawsync(lfs, file); + int err = lfs_file_sync_(lfs, file); #else int err = 0; #endif @@ -3274,7 +3294,7 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { #ifndef LFS_READONLY static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file) { file->off = file->pos; - lfs_alloc_ack(lfs); + lfs_alloc_ckpoint(lfs); int err = lfs_file_relocate(lfs, file); if (err) { return err; @@ -3366,7 +3386,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { } #ifndef LFS_READONLY -static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file) { +static int lfs_file_sync_(lfs_t *lfs, lfs_file_t *file) { if (file->flags & LFS_F_ERRED) { // it's not safe to do anything if our file errored return 0; @@ -3479,7 +3499,7 @@ static lfs_ssize_t lfs_file_flushedread(lfs_t *lfs, lfs_file_t *file, return size; } -static lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file, +static lfs_ssize_t lfs_file_read_(lfs_t *lfs, lfs_file_t *file, void *buffer, lfs_size_t size) { LFS_ASSERT((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY); @@ -3504,11 +3524,7 @@ static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file, lfs_size_t nsize = size; if ((file->flags & LFS_F_INLINE) && - lfs_max(file->pos+nsize, file->ctz.size) > - lfs_min(0x3fe, lfs_min( - lfs->cfg->cache_size, - (lfs->cfg->metadata_max ? - lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) { + lfs_max(file->pos+nsize, file->ctz.size) > lfs->inline_max) { // inline file doesn't fit anymore int err = lfs_file_outline(lfs, file); if (err) { @@ -3537,7 +3553,7 @@ static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file, } // extend file with new blocks - lfs_alloc_ack(lfs); + lfs_alloc_ckpoint(lfs); int err = lfs_ctz_extend(lfs, &file->cache, &lfs->rcache, file->block, file->pos, &file->block, &file->off); @@ -3580,13 +3596,13 @@ static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file, data += diff; nsize -= diff; - lfs_alloc_ack(lfs); + lfs_alloc_ckpoint(lfs); } return size; } -static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, +static lfs_ssize_t lfs_file_write_(lfs_t *lfs, lfs_file_t *file, const void *buffer, lfs_size_t size) { LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY); @@ -3630,7 +3646,7 @@ static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, } #endif -static lfs_soff_t lfs_file_rawseek(lfs_t *lfs, lfs_file_t *file, +static lfs_soff_t lfs_file_seek_(lfs_t *lfs, lfs_file_t *file, lfs_soff_t off, int whence) { // find new pos lfs_off_t npos = file->pos; @@ -3643,7 +3659,7 @@ static lfs_soff_t lfs_file_rawseek(lfs_t *lfs, lfs_file_t *file, npos = file->pos + off; } } else if (whence == LFS_SEEK_END) { - lfs_soff_t res = lfs_file_rawsize(lfs, file) + off; + lfs_soff_t res = lfs_file_size_(lfs, file) + off; if (res < 0) { return LFS_ERR_INVAL; } else { @@ -3694,7 +3710,7 @@ static lfs_soff_t lfs_file_rawseek(lfs_t *lfs, lfs_file_t *file, } #ifndef LFS_READONLY -static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { +static int lfs_file_truncate_(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY); if (size > LFS_FILE_MAX) { @@ -3702,15 +3718,12 @@ static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { } lfs_off_t pos = file->pos; - lfs_off_t oldsize = lfs_file_rawsize(lfs, file); + lfs_off_t oldsize = lfs_file_size_(lfs, file); if (size < oldsize) { // revert to inline file? - if (size <= lfs_min(0x3fe, lfs_min( - lfs->cfg->cache_size, - (lfs->cfg->metadata_max ? - lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) { + if (size <= lfs->inline_max) { // flush+seek to head - lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET); + lfs_soff_t res = lfs_file_seek_(lfs, file, 0, LFS_SEEK_SET); if (res < 0) { return (int)res; } @@ -3755,14 +3768,14 @@ static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { } } else if (size > oldsize) { // flush+seek if not already at end - lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_END); + lfs_soff_t res = lfs_file_seek_(lfs, file, 0, LFS_SEEK_END); if (res < 0) { return (int)res; } // fill with zeros while (file->pos < size) { - res = lfs_file_rawwrite(lfs, file, &(uint8_t){0}, 1); + res = lfs_file_write_(lfs, file, &(uint8_t){0}, 1); if (res < 0) { return (int)res; } @@ -3770,7 +3783,7 @@ static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { } // restore pos - lfs_soff_t res = lfs_file_rawseek(lfs, file, pos, LFS_SEEK_SET); + lfs_soff_t res = lfs_file_seek_(lfs, file, pos, LFS_SEEK_SET); if (res < 0) { return (int)res; } @@ -3779,13 +3792,13 @@ static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { } #endif -static lfs_soff_t lfs_file_rawtell(lfs_t *lfs, lfs_file_t *file) { +static lfs_soff_t lfs_file_tell_(lfs_t *lfs, lfs_file_t *file) { (void)lfs; return file->pos; } -static int lfs_file_rawrewind(lfs_t *lfs, lfs_file_t *file) { - lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET); +static int lfs_file_rewind_(lfs_t *lfs, lfs_file_t *file) { + lfs_soff_t res = lfs_file_seek_(lfs, file, 0, LFS_SEEK_SET); if (res < 0) { return (int)res; } @@ -3793,7 +3806,7 @@ static int lfs_file_rawrewind(lfs_t *lfs, lfs_file_t *file) { return 0; } -static lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file) { +static lfs_soff_t lfs_file_size_(lfs_t *lfs, lfs_file_t *file) { (void)lfs; #ifndef LFS_READONLY @@ -3807,7 +3820,7 @@ static lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file) { /// General fs operations /// -static int lfs_rawstat(lfs_t *lfs, const char *path, struct lfs_info *info) { +static int lfs_stat_(lfs_t *lfs, const char *path, struct lfs_info *info) { lfs_mdir_t cwd; lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); if (tag < 0) { @@ -3818,7 +3831,7 @@ static int lfs_rawstat(lfs_t *lfs, const char *path, struct lfs_info *info) { } #ifndef LFS_READONLY -static int lfs_rawremove(lfs_t *lfs, const char *path) { +static int lfs_remove_(lfs_t *lfs, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs_fs_forceconsistency(lfs); if (err) { @@ -3897,7 +3910,7 @@ static int lfs_rawremove(lfs_t *lfs, const char *path) { #endif #ifndef LFS_READONLY -static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) { +static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs_fs_forceconsistency(lfs); if (err) { @@ -3940,7 +3953,9 @@ static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) { newoldid += 1; } } else if (lfs_tag_type3(prevtag) != lfs_tag_type3(oldtag)) { - return LFS_ERR_ISDIR; + return (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) + ? LFS_ERR_ISDIR + : LFS_ERR_NOTDIR; } else if (samepair && newid == newoldid) { // we're renaming to ourselves?? return 0; @@ -4032,7 +4047,7 @@ static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) { } #endif -static lfs_ssize_t lfs_rawgetattr(lfs_t *lfs, const char *path, +static lfs_ssize_t lfs_getattr_(lfs_t *lfs, const char *path, uint8_t type, void *buffer, lfs_size_t size) { lfs_mdir_t cwd; lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); @@ -4090,7 +4105,7 @@ static int lfs_commitattr(lfs_t *lfs, const char *path, #endif #ifndef LFS_READONLY -static int lfs_rawsetattr(lfs_t *lfs, const char *path, +static int lfs_setattr_(lfs_t *lfs, const char *path, uint8_t type, const void *buffer, lfs_size_t size) { if (size > lfs->attr_max) { return LFS_ERR_NOSPC; @@ -4101,13 +4116,28 @@ static int lfs_rawsetattr(lfs_t *lfs, const char *path, #endif #ifndef LFS_READONLY -static int lfs_rawremoveattr(lfs_t *lfs, const char *path, uint8_t type) { +static int lfs_removeattr_(lfs_t *lfs, const char *path, uint8_t type) { return lfs_commitattr(lfs, path, type, NULL, 0x3ff); } #endif /// Filesystem operations /// + +// compile time checks, see lfs.h for why these limits exist +#if LFS_NAME_MAX > 1022 +#error "Invalid LFS_NAME_MAX, must be <= 1022" +#endif + +#if LFS_FILE_MAX > 2147483647 +#error "Invalid LFS_FILE_MAX, must be <= 2147483647" +#endif + +#if LFS_ATTR_MAX > 1022 +#error "Invalid LFS_ATTR_MAX, must be <= 1022" +#endif + +// common filesystem initialization static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->cfg = cfg; lfs->block_count = cfg->block_count; // May be 0 @@ -4155,6 +4185,14 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { // wear-leveling. LFS_ASSERT(lfs->cfg->block_cycles != 0); + // check that compact_thresh makes sense + // + // metadata can't be compacted below block_size/2, and metadata can't + // exceed a block_size + LFS_ASSERT(lfs->cfg->compact_thresh == 0 + || lfs->cfg->compact_thresh >= lfs->cfg->block_size/2); + LFS_ASSERT(lfs->cfg->compact_thresh == (lfs_size_t)-1 + || lfs->cfg->compact_thresh <= lfs->cfg->block_size); // setup read cache if (lfs->cfg->read_buffer) { @@ -4182,15 +4220,14 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs_cache_zero(lfs, &lfs->rcache); lfs_cache_zero(lfs, &lfs->pcache); - // setup lookahead, must be multiple of 64-bits, 32-bit aligned + // setup lookahead buffer, note mount finishes initializing this after + // we establish a decent pseudo-random seed LFS_ASSERT(lfs->cfg->lookahead_size > 0); - LFS_ASSERT(lfs->cfg->lookahead_size % 8 == 0 && - (uintptr_t)lfs->cfg->lookahead_buffer % 4 == 0); if (lfs->cfg->lookahead_buffer) { - lfs->free.buffer = lfs->cfg->lookahead_buffer; + lfs->lookahead.buffer = lfs->cfg->lookahead_buffer; } else { - lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead_size); - if (!lfs->free.buffer) { + lfs->lookahead.buffer = lfs_malloc(lfs->cfg->lookahead_size); + if (!lfs->lookahead.buffer) { err = LFS_ERR_NOMEM; goto cleanup; } @@ -4217,6 +4254,27 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { LFS_ASSERT(lfs->cfg->metadata_max <= lfs->cfg->block_size); + LFS_ASSERT(lfs->cfg->inline_max == (lfs_size_t)-1 + || lfs->cfg->inline_max <= lfs->cfg->cache_size); + LFS_ASSERT(lfs->cfg->inline_max == (lfs_size_t)-1 + || lfs->cfg->inline_max <= lfs->attr_max); + LFS_ASSERT(lfs->cfg->inline_max == (lfs_size_t)-1 + || lfs->cfg->inline_max <= ((lfs->cfg->metadata_max) + ? lfs->cfg->metadata_max + : lfs->cfg->block_size)/8); + lfs->inline_max = lfs->cfg->inline_max; + if (lfs->inline_max == (lfs_size_t)-1) { + lfs->inline_max = 0; + } else if (lfs->inline_max == 0) { + lfs->inline_max = lfs_min( + lfs->cfg->cache_size, + lfs_min( + lfs->attr_max, + ((lfs->cfg->metadata_max) + ? lfs->cfg->metadata_max + : lfs->cfg->block_size)/8)); + } + // setup default state lfs->root[0] = LFS_BLOCK_NULL; lfs->root[1] = LFS_BLOCK_NULL; @@ -4247,7 +4305,7 @@ static int lfs_deinit(lfs_t *lfs) { } if (!lfs->cfg->lookahead_buffer) { - lfs_free(lfs->free.buffer); + lfs_free(lfs->lookahead.buffer); } return 0; @@ -4256,7 +4314,7 @@ static int lfs_deinit(lfs_t *lfs) { #ifndef LFS_READONLY -static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) { +static int lfs_format_(lfs_t *lfs, const struct lfs_config *cfg) { int err = 0; { err = lfs_init(lfs, cfg); @@ -4267,12 +4325,12 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) { LFS_ASSERT(cfg->block_count != 0); // create free lookahead - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - lfs->free.off = 0; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, + memset(lfs->lookahead.buffer, 0, lfs->cfg->lookahead_size); + lfs->lookahead.start = 0; + lfs->lookahead.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->block_count); - lfs->free.i = 0; - lfs_alloc_ack(lfs); + lfs->lookahead.next = 0; + lfs_alloc_ckpoint(lfs); // create root dir lfs_mdir_t root; @@ -4323,7 +4381,7 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) { } #endif -static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { +static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) { int err = lfs_init(lfs, cfg); if (err) { return err; @@ -4440,6 +4498,9 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { } lfs->attr_max = superblock.attr_max; + + // we also need to update inline_max in case attr_max changed + lfs->inline_max = lfs_min(lfs->inline_max, lfs->attr_max); } // this is where we get the block_count from disk if block_count=0 @@ -4480,23 +4541,23 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { // setup free lookahead, to distribute allocations uniformly across // boots, we start the allocator at a random location - lfs->free.off = lfs->seed % lfs->block_count; + lfs->lookahead.start = lfs->seed % lfs->block_count; lfs_alloc_drop(lfs); return 0; cleanup: - lfs_rawunmount(lfs); + lfs_unmount_(lfs); return err; } -static int lfs_rawunmount(lfs_t *lfs) { +static int lfs_unmount_(lfs_t *lfs) { return lfs_deinit(lfs); } /// Filesystem filesystem operations /// -static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { +static int lfs_fs_stat_(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)) { @@ -4536,7 +4597,7 @@ static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { return 0; } -int lfs_fs_rawtraverse(lfs_t *lfs, +int lfs_fs_traverse_(lfs_t *lfs, int (*cb)(void *data, lfs_block_t block), void *data, bool includeorphans) { // iterate over metadata pairs @@ -5001,7 +5062,7 @@ static int lfs_fs_forceconsistency(lfs_t *lfs) { #endif #ifndef LFS_READONLY -static int lfs_fs_rawmkconsistent(lfs_t *lfs) { +static int lfs_fs_mkconsistent_(lfs_t *lfs) { // lfs_fs_forceconsistency does most of the work here int err = lfs_fs_forceconsistency(lfs); if (err) { @@ -5037,9 +5098,9 @@ static int lfs_fs_size_count(void *p, lfs_block_t block) { return 0; } -static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) { +static lfs_ssize_t lfs_fs_size_(lfs_t *lfs) { lfs_size_t size = 0; - int err = lfs_fs_rawtraverse(lfs, lfs_fs_size_count, &size, false); + int err = lfs_fs_traverse_(lfs, lfs_fs_size_count, &size, false); if (err) { return err; } @@ -5047,8 +5108,59 @@ static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) { return size; } +// explicit garbage collection #ifndef LFS_READONLY -static int lfs_fs_rawgrow(lfs_t *lfs, lfs_size_t block_count) { +static int lfs_fs_gc_(lfs_t *lfs) { + // force consistency, even if we're not necessarily going to write, + // because this function is supposed to take care of janitorial work + // isn't it? + int err = lfs_fs_forceconsistency(lfs); + if (err) { + return err; + } + + // try to compact metadata pairs, note we can't really accomplish + // anything if compact_thresh doesn't at least leave a prog_size + // available + if (lfs->cfg->compact_thresh + < lfs->cfg->block_size - lfs->cfg->prog_size) { + // iterate over all mdirs + lfs_mdir_t mdir = {.tail = {0, 1}}; + while (!lfs_pair_isnull(mdir.tail)) { + err = lfs_dir_fetch(lfs, &mdir, mdir.tail); + if (err) { + return err; + } + + // not erased? exceeds our compaction threshold? + if (!mdir.erased || ((lfs->cfg->compact_thresh == 0) + ? mdir.off > lfs->cfg->block_size - lfs->cfg->block_size/8 + : mdir.off > lfs->cfg->compact_thresh)) { + // the easiest way to trigger a compaction is to mark + // the mdir as unerased and add an empty commit + mdir.erased = false; + err = lfs_dir_commit(lfs, &mdir, NULL, 0); + if (err) { + return err; + } + } + } + } + + // try to populate the lookahead buffer, unless it's already full + if (lfs->lookahead.size < 8*lfs->cfg->lookahead_size) { + err = lfs_alloc_scan(lfs); + if (err) { + return err; + } + } + + return 0; +} +#endif + +#ifndef LFS_READONLY +static int lfs_fs_grow_(lfs_t *lfs, lfs_size_t block_count) { // shrinking is not supported LFS_ASSERT(block_count >= lfs->block_count); @@ -5453,10 +5565,10 @@ static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1, lfs->lfs1->root[1] = LFS_BLOCK_NULL; // setup free lookahead - lfs->free.off = 0; - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); + lfs->lookahead.start = 0; + lfs->lookahead.size = 0; + lfs->lookahead.next = 0; + lfs_alloc_ckpoint(lfs); // load superblock lfs1_dir_t dir; @@ -5507,7 +5619,7 @@ static int lfs1_unmount(lfs_t *lfs) { } /// v1 migration /// -static int lfs_rawmigrate(lfs_t *lfs, const struct lfs_config *cfg) { +static int lfs_migrate_(lfs_t *lfs, const struct lfs_config *cfg) { struct lfs1 lfs1; // Indeterminate filesystem size not allowed for migration. @@ -5774,7 +5886,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs_rawformat(lfs, cfg); + err = lfs_format_(lfs, cfg); LFS_TRACE("lfs_format -> %d", err); LFS_UNLOCK(cfg); @@ -5804,7 +5916,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs_rawmount(lfs, cfg); + err = lfs_mount_(lfs, cfg); LFS_TRACE("lfs_mount -> %d", err); LFS_UNLOCK(cfg); @@ -5818,7 +5930,7 @@ int lfs_unmount(lfs_t *lfs) { } LFS_TRACE("lfs_unmount(%p)", (void*)lfs); - err = lfs_rawunmount(lfs); + err = lfs_unmount_(lfs); LFS_TRACE("lfs_unmount -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5833,7 +5945,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { } LFS_TRACE("lfs_remove(%p, \"%s\")", (void*)lfs, path); - err = lfs_rawremove(lfs, path); + err = lfs_remove_(lfs, path); LFS_TRACE("lfs_remove -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5849,7 +5961,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { } LFS_TRACE("lfs_rename(%p, \"%s\", \"%s\")", (void*)lfs, oldpath, newpath); - err = lfs_rawrename(lfs, oldpath, newpath); + err = lfs_rename_(lfs, oldpath, newpath); LFS_TRACE("lfs_rename -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5864,7 +5976,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { } LFS_TRACE("lfs_stat(%p, \"%s\", %p)", (void*)lfs, path, (void*)info); - err = lfs_rawstat(lfs, path, info); + err = lfs_stat_(lfs, path, info); LFS_TRACE("lfs_stat -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5880,7 +5992,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, LFS_TRACE("lfs_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", (void*)lfs, path, type, buffer, size); - lfs_ssize_t res = lfs_rawgetattr(lfs, path, type, buffer, size); + lfs_ssize_t res = lfs_getattr_(lfs, path, type, buffer, size); LFS_TRACE("lfs_getattr -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -5897,7 +6009,7 @@ int lfs_setattr(lfs_t *lfs, const char *path, LFS_TRACE("lfs_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", (void*)lfs, path, type, buffer, size); - err = lfs_rawsetattr(lfs, path, type, buffer, size); + err = lfs_setattr_(lfs, path, type, buffer, size); LFS_TRACE("lfs_setattr -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5913,7 +6025,7 @@ int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) { } LFS_TRACE("lfs_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs, path, type); - err = lfs_rawremoveattr(lfs, path, type); + err = lfs_removeattr_(lfs, path, type); LFS_TRACE("lfs_removeattr -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5931,7 +6043,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags) { (void*)lfs, (void*)file, path, flags); LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - err = lfs_file_rawopen(lfs, file, path, flags); + err = lfs_file_open_(lfs, file, path, flags); LFS_TRACE("lfs_file_open -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5952,7 +6064,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - err = lfs_file_rawopencfg(lfs, file, path, flags, cfg); + err = lfs_file_opencfg_(lfs, file, path, flags, cfg); LFS_TRACE("lfs_file_opencfg -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5967,7 +6079,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - err = lfs_file_rawclose(lfs, file); + err = lfs_file_close_(lfs, file); LFS_TRACE("lfs_file_close -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -5983,7 +6095,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - err = lfs_file_rawsync(lfs, file); + err = lfs_file_sync_(lfs, file); LFS_TRACE("lfs_file_sync -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6001,7 +6113,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, (void*)lfs, (void*)file, buffer, size); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - lfs_ssize_t res = lfs_file_rawread(lfs, file, buffer, size); + lfs_ssize_t res = lfs_file_read_(lfs, file, buffer, size); LFS_TRACE("lfs_file_read -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -6019,7 +6131,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, (void*)lfs, (void*)file, buffer, size); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - lfs_ssize_t res = lfs_file_rawwrite(lfs, file, buffer, size); + lfs_ssize_t res = lfs_file_write_(lfs, file, buffer, size); LFS_TRACE("lfs_file_write -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -6037,7 +6149,7 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, (void*)lfs, (void*)file, off, whence); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - lfs_soff_t res = lfs_file_rawseek(lfs, file, off, whence); + lfs_soff_t res = lfs_file_seek_(lfs, file, off, whence); LFS_TRACE("lfs_file_seek -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -6054,7 +6166,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { (void*)lfs, (void*)file, size); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - err = lfs_file_rawtruncate(lfs, file, size); + err = lfs_file_truncate_(lfs, file, size); LFS_TRACE("lfs_file_truncate -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6070,7 +6182,7 @@ lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - lfs_soff_t res = lfs_file_rawtell(lfs, file); + lfs_soff_t res = lfs_file_tell_(lfs, file); LFS_TRACE("lfs_file_tell -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -6084,7 +6196,7 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { } LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file); - err = lfs_file_rawrewind(lfs, file); + err = lfs_file_rewind_(lfs, file); LFS_TRACE("lfs_file_rewind -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6099,7 +6211,7 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file); LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); - lfs_soff_t res = lfs_file_rawsize(lfs, file); + lfs_soff_t res = lfs_file_size_(lfs, file); LFS_TRACE("lfs_file_size -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -6114,7 +6226,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { } LFS_TRACE("lfs_mkdir(%p, \"%s\")", (void*)lfs, path); - err = lfs_rawmkdir(lfs, path); + err = lfs_mkdir_(lfs, path); LFS_TRACE("lfs_mkdir -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6130,7 +6242,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path); LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)dir)); - err = lfs_dir_rawopen(lfs, dir, path); + err = lfs_dir_open_(lfs, dir, path); LFS_TRACE("lfs_dir_open -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6144,7 +6256,7 @@ int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { } LFS_TRACE("lfs_dir_close(%p, %p)", (void*)lfs, (void*)dir); - err = lfs_dir_rawclose(lfs, dir); + err = lfs_dir_close_(lfs, dir); LFS_TRACE("lfs_dir_close -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6159,7 +6271,7 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { LFS_TRACE("lfs_dir_read(%p, %p, %p)", (void*)lfs, (void*)dir, (void*)info); - err = lfs_dir_rawread(lfs, dir, info); + err = lfs_dir_read_(lfs, dir, info); LFS_TRACE("lfs_dir_read -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6174,7 +6286,7 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { LFS_TRACE("lfs_dir_seek(%p, %p, %"PRIu32")", (void*)lfs, (void*)dir, off); - err = lfs_dir_rawseek(lfs, dir, off); + err = lfs_dir_seek_(lfs, dir, off); LFS_TRACE("lfs_dir_seek -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6188,7 +6300,7 @@ lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) { } LFS_TRACE("lfs_dir_tell(%p, %p)", (void*)lfs, (void*)dir); - lfs_soff_t res = lfs_dir_rawtell(lfs, dir); + lfs_soff_t res = lfs_dir_tell_(lfs, dir); LFS_TRACE("lfs_dir_tell -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -6202,7 +6314,7 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) { } LFS_TRACE("lfs_dir_rewind(%p, %p)", (void*)lfs, (void*)dir); - err = lfs_dir_rawrewind(lfs, dir); + err = lfs_dir_rewind_(lfs, dir); LFS_TRACE("lfs_dir_rewind -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6216,7 +6328,7 @@ int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { } LFS_TRACE("lfs_fs_stat(%p, %p)", (void*)lfs, (void*)fsinfo); - err = lfs_fs_rawstat(lfs, fsinfo); + err = lfs_fs_stat_(lfs, fsinfo); LFS_TRACE("lfs_fs_stat -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6230,7 +6342,7 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs) { } LFS_TRACE("lfs_fs_size(%p)", (void*)lfs); - lfs_ssize_t res = lfs_fs_rawsize(lfs); + lfs_ssize_t res = lfs_fs_size_(lfs); LFS_TRACE("lfs_fs_size -> %"PRId32, res); LFS_UNLOCK(lfs->cfg); @@ -6245,7 +6357,7 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void *, lfs_block_t), void *data) { LFS_TRACE("lfs_fs_traverse(%p, %p, %p)", (void*)lfs, (void*)(uintptr_t)cb, data); - err = lfs_fs_rawtraverse(lfs, cb, data, true); + err = lfs_fs_traverse_(lfs, cb, data, true); LFS_TRACE("lfs_fs_traverse -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6253,32 +6365,32 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void *, lfs_block_t), void *data) { } #ifndef LFS_READONLY -int lfs_fs_gc(lfs_t *lfs) { +int lfs_fs_mkconsistent(lfs_t *lfs) { int err = LFS_LOCK(lfs->cfg); if (err) { return err; } - LFS_TRACE("lfs_fs_gc(%p)", (void*)lfs); + LFS_TRACE("lfs_fs_mkconsistent(%p)", (void*)lfs); - err = lfs_fs_rawgc(lfs); + err = lfs_fs_mkconsistent_(lfs); - LFS_TRACE("lfs_fs_gc -> %d", err); + LFS_TRACE("lfs_fs_mkconsistent -> %d", err); LFS_UNLOCK(lfs->cfg); return err; } #endif #ifndef LFS_READONLY -int lfs_fs_mkconsistent(lfs_t *lfs) { +int lfs_fs_gc(lfs_t *lfs) { int err = LFS_LOCK(lfs->cfg); if (err) { return err; } - LFS_TRACE("lfs_fs_mkconsistent(%p)", (void*)lfs); + LFS_TRACE("lfs_fs_gc(%p)", (void*)lfs); - err = lfs_fs_rawmkconsistent(lfs); + err = lfs_fs_gc_(lfs); - LFS_TRACE("lfs_fs_mkconsistent -> %d", err); + LFS_TRACE("lfs_fs_gc -> %d", err); LFS_UNLOCK(lfs->cfg); return err; } @@ -6292,7 +6404,7 @@ int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count) { } LFS_TRACE("lfs_fs_grow(%p, %"PRIu32")", (void*)lfs, block_count); - err = lfs_fs_rawgrow(lfs, block_count); + err = lfs_fs_grow_(lfs, block_count); LFS_TRACE("lfs_fs_grow -> %d", err); LFS_UNLOCK(lfs->cfg); @@ -6323,7 +6435,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs_rawmigrate(lfs, cfg); + err = lfs_migrate_(lfs, cfg); LFS_TRACE("lfs_migrate -> %d", err); LFS_UNLOCK(cfg); diff --git a/lfs.h b/lfs.h index 9eeab23..9914502 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 0x00020008 +#define LFS_VERSION 0x00020009 #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) @@ -52,10 +52,8 @@ typedef uint32_t lfs_block_t; #endif // Maximum size of a file in bytes, may be redefined to limit to support other -// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the -// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return -// incorrect values due to using signed integers. Stored in superblock and -// must be respected by other littlefs drivers. +// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be +// respected by other littlefs drivers. #ifndef LFS_FILE_MAX #define LFS_FILE_MAX 2147483647 #endif @@ -226,9 +224,20 @@ struct lfs_config { // Size of the lookahead buffer in bytes. A larger lookahead buffer // increases the number of blocks found during an allocation pass. The // lookahead buffer is stored as a compact bitmap, so each byte of RAM - // can track 8 blocks. Must be a multiple of 8. + // can track 8 blocks. lfs_size_t lookahead_size; + // Threshold for metadata compaction during lfs_fs_gc in bytes. Metadata + // pairs that exceed this threshold will be compacted during lfs_fs_gc. + // Defaults to ~88% block_size when zero, though the default may change + // in the future. + // + // Note this only affects lfs_fs_gc. Normal compactions still only occur + // when full. + // + // Set to -1 to disable metadata compaction during lfs_fs_gc. + lfs_size_t compact_thresh; + // Optional statically allocated read buffer. Must be cache_size. // By default lfs_malloc is used to allocate this buffer. void *read_buffer; @@ -237,9 +246,8 @@ struct lfs_config { // By default lfs_malloc is used to allocate this buffer. void *prog_buffer; - // Optional statically allocated lookahead buffer. Must be lookahead_size - // and aligned to a 32-bit boundary. By default lfs_malloc is used to - // allocate this buffer. + // Optional statically allocated lookahead buffer. Must be lookahead_size. + // By default lfs_malloc is used to allocate this buffer. void *lookahead_buffer; // Optional upper limit on length of file names in bytes. No downside for @@ -264,6 +272,15 @@ struct lfs_config { // Defaults to block_size when zero. lfs_size_t metadata_max; + // Optional upper limit on inlined files in bytes. Inlined files live in + // metadata and decrease storage requirements, but may be limited to + // improve metadata-related performance. Must be <= cache_size, <= + // attr_max, and <= block_size/8. Defaults to the largest possible + // inline_max when zero. + // + // Set to -1 to disable inlined files. + lfs_size_t inline_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 @@ -430,19 +447,20 @@ typedef struct lfs { lfs_gstate_t gdisk; lfs_gstate_t gdelta; - struct lfs_free { - lfs_block_t off; + struct lfs_lookahead { + lfs_block_t start; lfs_block_t size; - lfs_block_t i; - lfs_block_t ack; - uint32_t *buffer; - } free; + lfs_block_t next; + lfs_block_t ckpoint; + uint8_t *buffer; + } lookahead; const struct lfs_config *cfg; lfs_size_t block_count; lfs_size_t name_max; lfs_size_t file_max; lfs_size_t attr_max; + lfs_size_t inline_max; #ifdef LFS_MIGRATE struct lfs1 *lfs1; @@ -712,18 +730,6 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs); // Returns a negative error code on failure. int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); -// Attempt to proactively find free blocks -// -// Calling this function is not required, but may allowing the offloading of -// the expensive block allocation scan to a less time-critical code path. -// -// Note: littlefs currently does not persist any found free blocks to disk. -// This may change in the future. -// -// Returns a negative error code on failure. Finding no free blocks is -// not an error. -int lfs_fs_gc(lfs_t *lfs); - #ifndef LFS_READONLY // Attempt to make the filesystem consistent and ready for writing // @@ -736,6 +742,24 @@ int lfs_fs_gc(lfs_t *lfs); int lfs_fs_mkconsistent(lfs_t *lfs); #endif +#ifndef LFS_READONLY +// Attempt any janitorial work +// +// This currently: +// 1. Calls mkconsistent if not already consistent +// 2. Compacts metadata > compact_thresh +// 3. Populates the block allocator +// +// Though additional janitorial work may be added in the future. +// +// Calling this function is not required, but may allow the offloading of +// expensive janitorial work to a less time-critical code path. +// +// Returns a negative error code on failure. Accomplishing nothing is not +// an error. +int lfs_fs_gc(lfs_t *lfs); +#endif + #ifndef LFS_READONLY // Grows the filesystem to a new size, updating the superblock with the new // block count. diff --git a/lfs_util.c b/lfs_util.c index 9cdd1c6..dac72ab 100644 --- a/lfs_util.c +++ b/lfs_util.c @@ -11,6 +11,8 @@ #ifndef LFS_CONFIG +// If user provides their own CRC impl we don't need this +#ifndef LFS_CRC // Software CRC implementation with small lookup table uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { static const uint32_t rtable[16] = { @@ -29,6 +31,7 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { return crc; } +#endif #endif diff --git a/lfs_util.h b/lfs_util.h index 13e9396..4e57700 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -212,12 +212,22 @@ static inline uint32_t lfs_tobe32(uint32_t a) { } // Calculate CRC-32 with polynomial = 0x04c11db7 +#ifdef LFS_CRC +uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { + return LFS_CRC(crc, buffer, size) +} +#else uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); +#endif // Allocate memory, only used if buffers are not provided to littlefs -// Note, memory must be 64-bit aligned +// +// littlefs current has no alignment requirements, as it only allocates +// byte-level buffers. static inline void *lfs_malloc(size_t size) { -#ifndef LFS_NO_MALLOC +#if defined(LFS_MALLOC) + return LFS_MALLOC(size); +#elif !defined(LFS_NO_MALLOC) return malloc(size); #else (void)size; @@ -227,7 +237,9 @@ static inline void *lfs_malloc(size_t size) { // Deallocate memory, only used if buffers are not provided to littlefs static inline void lfs_free(void *p) { -#ifndef LFS_NO_MALLOC +#if defined(LFS_FREE) + LFS_FREE(p); +#elif !defined(LFS_NO_MALLOC) free(p); #else (void)p; diff --git a/runners/bench_runner.c b/runners/bench_runner.c index f4dce22..387889d 100644 --- a/runners/bench_runner.c +++ b/runners/bench_runner.c @@ -1321,6 +1321,8 @@ void perm_run( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .compact_thresh = COMPACT_THRESH, + .inline_max = INLINE_MAX, }; struct lfs_emubd_config bdcfg = { diff --git a/runners/bench_runner.h b/runners/bench_runner.h index b072970..174733c 100644 --- a/runners/bench_runner.h +++ b/runners/bench_runner.h @@ -95,11 +95,13 @@ intmax_t bench_define(size_t define); #define BLOCK_COUNT_i 5 #define CACHE_SIZE_i 6 #define LOOKAHEAD_SIZE_i 7 -#define BLOCK_CYCLES_i 8 -#define ERASE_VALUE_i 9 -#define ERASE_CYCLES_i 10 -#define BADBLOCK_BEHAVIOR_i 11 -#define POWERLOSS_BEHAVIOR_i 12 +#define COMPACT_THRESH_i 8 +#define INLINE_MAX_i 9 +#define BLOCK_CYCLES_i 10 +#define ERASE_VALUE_i 11 +#define ERASE_CYCLES_i 12 +#define BADBLOCK_BEHAVIOR_i 13 +#define POWERLOSS_BEHAVIOR_i 14 #define READ_SIZE bench_define(READ_SIZE_i) #define PROG_SIZE bench_define(PROG_SIZE_i) @@ -109,6 +111,8 @@ intmax_t bench_define(size_t define); #define BLOCK_COUNT bench_define(BLOCK_COUNT_i) #define CACHE_SIZE bench_define(CACHE_SIZE_i) #define LOOKAHEAD_SIZE bench_define(LOOKAHEAD_SIZE_i) +#define COMPACT_THRESH bench_define(COMPACT_THRESH_i) +#define INLINE_MAX bench_define(INLINE_MAX_i) #define BLOCK_CYCLES bench_define(BLOCK_CYCLES_i) #define ERASE_VALUE bench_define(ERASE_VALUE_i) #define ERASE_CYCLES bench_define(ERASE_CYCLES_i) @@ -124,6 +128,8 @@ intmax_t bench_define(size_t define); BENCH_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1))\ BENCH_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \ BENCH_DEF(LOOKAHEAD_SIZE, 16) \ + BENCH_DEF(COMPACT_THRESH, 0) \ + BENCH_DEF(INLINE_MAX, 0) \ BENCH_DEF(BLOCK_CYCLES, -1) \ BENCH_DEF(ERASE_VALUE, 0xff) \ BENCH_DEF(ERASE_CYCLES, 0) \ @@ -131,7 +137,7 @@ intmax_t bench_define(size_t define); BENCH_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP) #define BENCH_GEOMETRY_DEFINE_COUNT 4 -#define BENCH_IMPLICIT_DEFINE_COUNT 13 +#define BENCH_IMPLICIT_DEFINE_COUNT 15 #endif diff --git a/runners/test_runner.c b/runners/test_runner.c index 13befdc..ff52673 100644 --- a/runners/test_runner.c +++ b/runners/test_runner.c @@ -1346,6 +1346,8 @@ static void run_powerloss_none( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .compact_thresh = COMPACT_THRESH, + .inline_max = INLINE_MAX, #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, #endif @@ -1422,6 +1424,8 @@ static void run_powerloss_linear( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .compact_thresh = COMPACT_THRESH, + .inline_max = INLINE_MAX, #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, #endif @@ -1515,6 +1519,8 @@ static void run_powerloss_log( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .compact_thresh = COMPACT_THRESH, + .inline_max = INLINE_MAX, #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, #endif @@ -1606,6 +1612,8 @@ static void run_powerloss_cycles( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .compact_thresh = COMPACT_THRESH, + .inline_max = INLINE_MAX, #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, #endif @@ -1795,6 +1803,8 @@ static void run_powerloss_exhaustive( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .compact_thresh = COMPACT_THRESH, + .inline_max = INLINE_MAX, #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, #endif diff --git a/runners/test_runner.h b/runners/test_runner.h index 4be72e4..0f0e594 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -88,12 +88,14 @@ intmax_t test_define(size_t define); #define BLOCK_COUNT_i 5 #define CACHE_SIZE_i 6 #define LOOKAHEAD_SIZE_i 7 -#define BLOCK_CYCLES_i 8 -#define ERASE_VALUE_i 9 -#define ERASE_CYCLES_i 10 -#define BADBLOCK_BEHAVIOR_i 11 -#define POWERLOSS_BEHAVIOR_i 12 -#define DISK_VERSION_i 13 +#define COMPACT_THRESH_i 8 +#define INLINE_MAX_i 9 +#define BLOCK_CYCLES_i 10 +#define ERASE_VALUE_i 11 +#define ERASE_CYCLES_i 12 +#define BADBLOCK_BEHAVIOR_i 13 +#define POWERLOSS_BEHAVIOR_i 14 +#define DISK_VERSION_i 15 #define READ_SIZE TEST_DEFINE(READ_SIZE_i) #define PROG_SIZE TEST_DEFINE(PROG_SIZE_i) @@ -103,6 +105,8 @@ intmax_t test_define(size_t define); #define BLOCK_COUNT TEST_DEFINE(BLOCK_COUNT_i) #define CACHE_SIZE TEST_DEFINE(CACHE_SIZE_i) #define LOOKAHEAD_SIZE TEST_DEFINE(LOOKAHEAD_SIZE_i) +#define COMPACT_THRESH TEST_DEFINE(COMPACT_THRESH_i) +#define INLINE_MAX TEST_DEFINE(INLINE_MAX_i) #define BLOCK_CYCLES TEST_DEFINE(BLOCK_CYCLES_i) #define ERASE_VALUE TEST_DEFINE(ERASE_VALUE_i) #define ERASE_CYCLES TEST_DEFINE(ERASE_CYCLES_i) @@ -119,6 +123,8 @@ intmax_t test_define(size_t define); TEST_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1)) \ TEST_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \ TEST_DEF(LOOKAHEAD_SIZE, 16) \ + TEST_DEF(COMPACT_THRESH, 0) \ + TEST_DEF(INLINE_MAX, 0) \ TEST_DEF(BLOCK_CYCLES, -1) \ TEST_DEF(ERASE_VALUE, 0xff) \ TEST_DEF(ERASE_CYCLES, 0) \ @@ -127,7 +133,7 @@ intmax_t test_define(size_t define); TEST_DEF(DISK_VERSION, 0) #define TEST_GEOMETRY_DEFINE_COUNT 4 -#define TEST_IMPLICIT_DEFINE_COUNT 14 +#define TEST_IMPLICIT_DEFINE_COUNT 16 #endif diff --git a/tests/test_alloc.toml b/tests/test_alloc.toml index e6fba97..9e4daee 100644 --- a/tests/test_alloc.toml +++ b/tests/test_alloc.toml @@ -7,6 +7,7 @@ if = 'BLOCK_CYCLES == -1' defines.FILES = 3 defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' defines.GC = [false, true] +defines.COMPACT_THRESH = ['-1', '0', 'BLOCK_SIZE/2'] code = ''' const char *names[] = {"bacon", "eggs", "pancakes"}; lfs_file_t files[FILES]; @@ -60,6 +61,7 @@ code = ''' defines.FILES = 3 defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' defines.GC = [false, true] +defines.COMPACT_THRESH = ['-1', '0', 'BLOCK_SIZE/2'] code = ''' const char *names[] = {"bacon", "eggs", "pancakes"}; diff --git a/tests/test_dirs.toml b/tests/test_dirs.toml index 4262a1a..181dd6a 100644 --- a/tests/test_dirs.toml +++ b/tests/test_dirs.toml @@ -747,6 +747,11 @@ code = ''' lfs_file_open(&lfs, &file, "potato", LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR; + lfs_file_open(&lfs, &file, "tacoto", LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_rename(&lfs, "tacoto", "potato") => LFS_ERR_ISDIR; + lfs_rename(&lfs, "potato", "tacoto") => LFS_ERR_NOTDIR; + lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST; lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; @@ -770,6 +775,10 @@ code = ''' lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "potato") == 0); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(info.type == LFS_TYPE_REG); + assert(strcmp(info.name, "tacoto") == 0); + assert(info.size == 0); lfs_dir_read(&lfs, &dir, &info) => 0; lfs_dir_close(&lfs, &dir) => 0; @@ -790,6 +799,10 @@ code = ''' lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "potato") == 0); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(info.type == LFS_TYPE_REG); + assert(strcmp(info.name, "tacoto") == 0); + assert(info.size == 0); lfs_dir_read(&lfs, &dir, &info) => 0; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; diff --git a/tests/test_files.toml b/tests/test_files.toml index afb0811..1c86cd8 100644 --- a/tests/test_files.toml +++ b/tests/test_files.toml @@ -1,5 +1,6 @@ [cases.test_files_simple] +defines.INLINE_MAX = [0, -1, 8] code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; @@ -25,6 +26,7 @@ code = ''' [cases.test_files_large] defines.SIZE = [32, 8192, 262144, 0, 7, 8193] defines.CHUNKSIZE = [31, 16, 33, 1, 1023] +defines.INLINE_MAX = [0, -1, 8] code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; @@ -67,6 +69,7 @@ code = ''' defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] defines.CHUNKSIZE = [31, 16, 1] +defines.INLINE_MAX = [0, -1, 8] code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; @@ -152,6 +155,7 @@ code = ''' defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] defines.CHUNKSIZE = [31, 16, 1] +defines.INLINE_MAX = [0, -1, 8] code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; @@ -232,6 +236,7 @@ code = ''' defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] defines.CHUNKSIZE = [31, 16, 1] +defines.INLINE_MAX = [0, -1, 8] code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; @@ -303,6 +308,7 @@ code = ''' [cases.test_files_reentrant_write] defines.SIZE = [32, 0, 7, 2049] defines.CHUNKSIZE = [31, 16, 65] +defines.INLINE_MAX = [0, -1, 8] reentrant = true code = ''' lfs_t lfs; @@ -354,11 +360,20 @@ code = ''' [cases.test_files_reentrant_write_sync] defines = [ # append (O(n)) - {MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]}, + {MODE='LFS_O_APPEND', + SIZE=[32, 0, 7, 2049], + CHUNKSIZE=[31, 16, 65], + INLINE_MAX=[0, -1, 8]}, # truncate (O(n^2)) - {MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, + {MODE='LFS_O_TRUNC', + SIZE=[32, 0, 7, 200], + CHUNKSIZE=[31, 16, 65], + INLINE_MAX=[0, -1, 8]}, # rewrite (O(n^2)) - {MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, + {MODE=0, + SIZE=[32, 0, 7, 200], + CHUNKSIZE=[31, 16, 65], + INLINE_MAX=[0, -1, 8]}, ] reentrant = true code = ''' diff --git a/tests/test_orphans.toml b/tests/test_orphans.toml index 2c8405a..d7040ed 100644 --- a/tests/test_orphans.toml +++ b/tests/test_orphans.toml @@ -98,7 +98,7 @@ code = ''' lfs_mount(&lfs, cfg) => 0; // create an orphan lfs_mdir_t orphan; - lfs_alloc_ack(&lfs); + lfs_alloc_ckpoint(&lfs); lfs_dir_alloc(&lfs, &orphan) => 0; lfs_dir_commit(&lfs, &orphan, NULL, 0) => 0; @@ -170,7 +170,7 @@ code = ''' lfs_mount(&lfs, cfg) => 0; // create an orphan lfs_mdir_t orphan; - lfs_alloc_ack(&lfs); + lfs_alloc_ckpoint(&lfs); lfs_dir_alloc(&lfs, &orphan) => 0; lfs_dir_commit(&lfs, &orphan, NULL, 0) => 0;