Explorar el Código

Merge remote-tracking branch 'origin/master' into debug-improvements

Christopher Haster hace 6 años
padre
commit
e1f3b90b56
Se han modificado 8 ficheros con 229 adiciones y 129 borrados
  1. 14 2
      .travis.yml
  2. 1 1
      Makefile
  3. 2 3
      emubd/lfs_emubd.c
  4. 2 14
      emubd/lfs_emubd.h
  5. 131 94
      lfs.c
  6. 12 7
      lfs.h
  7. 1 1
      scripts/template.fmt
  8. 66 7
      tests/test_files.sh

+ 14 - 2
.travis.yml

@@ -23,8 +23,20 @@ script:
   - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
 
   - make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
+  - make clean test QUIET=1 CFLAGS+="-DLFS_EMUBD_ERASE_VALUE=0xff"
   - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS"
 
+  # additional configurations that don't support all tests (this should be
+  # fixed but at the moment it is what it is)
+  - make test_files QUIET=1
+        CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096"
+  - make test_files QUIET=1
+        CFLAGS+="-DLFS_READ_SIZE=\(2*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)"
+  - make test_files QUIET=1
+        CFLAGS+="-DLFS_READ_SIZE=\(8*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)"
+  - make test_files QUIET=1
+        CFLAGS+="-DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704"
+
   # compile and find the code size with the smallest configuration
   - make clean size
         OBJ="$(ls lfs*.o | tr '\n' ' ')"
@@ -111,7 +123,7 @@ jobs:
       if: branch !~ -prefix$
       install:
         - sudo apt-get install libfuse-dev
-        - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha
+        - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2
         - fusermount -V
         - gcc --version
       before_script:
@@ -146,7 +158,7 @@ jobs:
       if: branch !~ -prefix$
       install:
         - sudo apt-get install libfuse-dev
-        - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha v2
+        - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2
         - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1
         - fusermount -V
         - gcc --version

+ 1 - 1
Makefile

@@ -60,7 +60,7 @@ test: \
 test_%: tests/test_%.sh
 
 ifdef QUIET
-	@./$< | sed -n '/^[-=]/p'
+	@./$< | sed -nu '/^[-=]/p'
 else
 	./$<
 endif

+ 2 - 3
emubd/lfs_emubd.c

@@ -11,7 +11,6 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <limits.h>
-#include <dirent.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <assert.h>
@@ -96,7 +95,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
     snprintf(emu->child, LFS_NAME_MAX, ".stats");
     FILE *f = fopen(emu->path, "r");
     if (!f) {
-        memset(&emu->stats, 0, sizeof(emu->stats));
+        memset(&emu->stats, LFS_EMUBD_ERASE_VALUE, sizeof(emu->stats));
     } else {
         size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
         lfs_emubd_fromle32(emu);
@@ -265,7 +264,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
 
     // update history and stats
     if (block != emu->history.blocks[0]) {
-        memcpy(&emu->history.blocks[1], &emu->history.blocks[0],
+        memmove(&emu->history.blocks[1], &emu->history.blocks[0],
                 sizeof(emu->history) - sizeof(emu->history.blocks[0]));
         emu->history.blocks[0] = block;
     }

+ 2 - 14
emubd/lfs_emubd.h

@@ -17,20 +17,8 @@ extern "C"
 
 
 // Config options
-#ifndef LFS_EMUBD_READ_SIZE
-#define LFS_EMUBD_READ_SIZE 1
-#endif
-
-#ifndef LFS_EMUBD_PROG_SIZE
-#define LFS_EMUBD_PROG_SIZE 1
-#endif
-
-#ifndef LFS_EMUBD_ERASE_SIZE
-#define LFS_EMUBD_ERASE_SIZE 512
-#endif
-
-#ifndef LFS_EMUBD_TOTAL_SIZE
-#define LFS_EMUBD_TOTAL_SIZE 524288
+#ifndef LFS_EMUBD_ERASE_VALUE
+#define LFS_EMUBD_ERASE_VALUE 0x00
 #endif
 
 

+ 131 - 94
lfs.c

@@ -1,19 +1,8 @@
 /*
  * The little filesystem
  *
- * Copyright (c) 2017 ARM Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
  */
 #include "lfs.h"
 #include "lfs_util.h"
@@ -420,7 +409,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
 static int lfs_dir_compact(lfs_t *lfs,
         lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
         lfs_mdir_t *source, uint16_t begin, uint16_t end);
-static int lfs_file_relocate(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);
 static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans);
 static void lfs_fs_prepmove(lfs_t *lfs,
@@ -636,11 +625,17 @@ static int lfs_dir_traverse_filter(void *p,
     lfs_tag_t *filtertag = p;
     (void)buffer;
 
+    // which mask depends on unique bit in tag structure
+    uint32_t mask = (tag & LFS_MKTAG(0x100, 0, 0))
+            ? LFS_MKTAG(0x7ff, 0x3ff, 0)
+            : LFS_MKTAG(0x700, 0x3ff, 0);
+
     // check for redundancy
-    uint32_t mask = LFS_MKTAG(0x7ff, 0x3ff, 0);
     if ((mask & tag) == (mask & *filtertag) ||
-        (mask & tag) == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) |
-            (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) {
+            lfs_tag_isdelete(*filtertag) ||
+            (LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == (
+                LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) |
+                    (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) {
         return true;
     }
 
@@ -1238,65 +1233,85 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
 
 static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
     // align to program units
-    lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t),
+    const lfs_off_t off1 = commit->off + sizeof(lfs_tag_t);
+    const lfs_off_t end = lfs_alignup(off1 + sizeof(uint32_t),
             lfs->cfg->prog_size);
 
-    // read erased state from next program unit
-    lfs_tag_t tag;
-    int err = lfs_bd_read(lfs,
-            NULL, &lfs->rcache, sizeof(tag),
-            commit->block, off, &tag, sizeof(tag));
-    if (err && err != LFS_ERR_CORRUPT) {
-        return err;
-    }
-
-    // build crc tag
-    bool reset = ~lfs_frombe32(tag) >> 31;
-    tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff,
-        off - (commit->off+sizeof(lfs_tag_t)));
+    // create crc tags to fill up remainder of commit, note that
+    // padding is not crcd, which lets fetches skip padding but
+    // makes committing a bit more complicated
+    while (commit->off < end) {
+        lfs_off_t off = commit->off + sizeof(lfs_tag_t);
+        lfs_off_t noff = lfs_min(end - off, 0x3fe) + off;
+        if (noff < end) {
+            noff = lfs_min(noff, end - 2*sizeof(uint32_t));
+        }
 
-    // write out crc
-    uint32_t footer[2];
-    footer[0] = lfs_tobe32(tag ^ commit->ptag);
-    commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0]));
-    footer[1] = lfs_tole32(commit->crc);
-    err = lfs_bd_prog(lfs,
-            &lfs->pcache, &lfs->rcache, false,
-            commit->block, commit->off, &footer, sizeof(footer));
-    if (err) {
-        return err;
-    }
-    commit->off += sizeof(tag)+lfs_tag_size(tag);
-    commit->ptag = tag ^ (reset << 31);
+        // read erased state from next program unit
+        lfs_tag_t tag = 0xffffffff;
+        int err = lfs_bd_read(lfs,
+                NULL, &lfs->rcache, sizeof(tag),
+                commit->block, noff, &tag, sizeof(tag));
+        if (err && err != LFS_ERR_CORRUPT) {
+            return err;
+        }
 
-    // flush buffers
-    err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false);
-    if (err) {
-        return err;
-    }
+        // build crc tag
+        bool reset = ~lfs_frombe32(tag) >> 31;
+        tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, noff - off);
 
-    // successful commit, check checksum to make sure
-    uint32_t crc = 0xffffffff;
-    lfs_size_t size = commit->off - lfs_tag_size(tag) - commit->begin;
-    for (lfs_off_t i = 0; i < size; i++) {
-        // leave it up to caching to make this efficient
-        uint8_t dat;
-        err = lfs_bd_read(lfs,
-                NULL, &lfs->rcache, size-i,
-                commit->block, commit->begin+i, &dat, 1);
+        // write out crc
+        uint32_t footer[2];
+        footer[0] = lfs_tobe32(tag ^ commit->ptag);
+        commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0]));
+        footer[1] = lfs_tole32(commit->crc);
+        err = lfs_bd_prog(lfs,
+                &lfs->pcache, &lfs->rcache, false,
+                commit->block, commit->off, &footer, sizeof(footer));
         if (err) {
             return err;
         }
 
-        crc = lfs_crc(crc, &dat, 1);
+        commit->off += sizeof(tag)+lfs_tag_size(tag);
+        commit->ptag = tag ^ (reset << 31);
+        commit->crc = 0xffffffff; // reset crc for next "commit"
     }
 
+    // flush buffers
+    int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false);
     if (err) {
         return err;
     }
 
-    if (crc != commit->crc) {
-        return LFS_ERR_CORRUPT;
+    // successful commit, check checksums to make sure
+    lfs_off_t off = commit->begin;
+    lfs_off_t noff = off1;
+    while (off < end) {
+        uint32_t crc = 0xffffffff;
+        for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) {
+            // leave it up to caching to make this efficient
+            uint8_t dat;
+            err = lfs_bd_read(lfs,
+                    NULL, &lfs->rcache, noff+sizeof(uint32_t)-i,
+                    commit->block, i, &dat, 1);
+            if (err) {
+                return err;
+            }
+
+            crc = lfs_crc(crc, &dat, 1);
+        }
+
+        // detected write error?
+        if (crc != 0) {
+            return LFS_ERR_CORRUPT;
+        }
+
+        // skip padding
+        off = lfs_min(end - noff, 0x3fe) + noff;
+        if (off < end) {
+            off = lfs_min(off, end - 2*sizeof(uint32_t));
+        }
+        noff = off + sizeof(uint32_t);
     }
 
     return 0;
@@ -1459,7 +1474,7 @@ static int lfs_dir_compact(lfs_t *lfs,
 
     // increment revision count
     dir->rev += 1;
-    if (lfs->cfg->block_cycles &&
+    if (lfs->cfg->block_cycles > 0 &&
             (dir->rev % (lfs->cfg->block_cycles+1) == 0)) {
         if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
             // oh no! we're writing too much to the superblock,
@@ -1589,11 +1604,11 @@ static int lfs_dir_compact(lfs_t *lfs,
             }
 
             // successful compaction, swap dir pair to indicate most recent
+            LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
             lfs_pair_swap(dir->pair);
             dir->count = end - begin;
             dir->off = commit.off;
             dir->etag = commit.ptag;
-            dir->erased = (dir->off % lfs->cfg->prog_size == 0);
             // note we able to have already handled move here
             if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) {
                 lfs_gstate_xormove(&lfs->gpending,
@@ -1649,11 +1664,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
         if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 &&
                 f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) &&
                 f->ctz.size > lfs->cfg->cache_size) {
-            f->flags &= ~LFS_F_READING;
-            f->off = 0;
-
-            lfs_alloc_ack(lfs);
-            int err = lfs_file_relocate(lfs, f);
+            int err = lfs_file_outline(lfs, f);
             if (err) {
                 return err;
             }
@@ -1764,6 +1775,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
         }
 
         // successful commit, update dir
+        LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
         dir->off = commit.off;
         dir->etag = commit.ptag;
 
@@ -2295,6 +2307,10 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
                  ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})",
             (void*)lfs, (void*)file, path, flags,
             (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count);
+
+    // do not allow open for already opened file
+    LFS_ASSERT(0 == (file->flags & LFS_F_OPENED));
+
     // deorphan if we haven't yet, needed at most once after poweron
     if ((flags & 3) != LFS_O_RDONLY) {
         int err = lfs_fs_forceconsistency(lfs);
@@ -2307,7 +2323,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
     // setup simple file details
     int err;
     file->cfg = cfg;
-    file->flags = flags;
+    file->flags = flags | LFS_F_OPENED;
     file->pos = 0;
     file->cache.buffer = NULL;
 
@@ -2452,6 +2468,8 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
 
 int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
     LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file);
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+
     int err = lfs_file_sync(lfs, file);
 
     // remove from list of mdirs
@@ -2467,11 +2485,14 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
         lfs_free(file->cache.buffer);
     }
 
+    file->flags &= ~LFS_F_OPENED;
     LFS_TRACE("lfs_file_close -> %d", err);
     return err;
 }
 
 static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+
     while (true) {
         // just relocate what exists into new block
         lfs_block_t nblock;
@@ -2529,7 +2550,6 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
         lfs_cache_zero(lfs, &lfs->pcache);
 
         file->block = nblock;
-        file->flags &= ~LFS_F_INLINE;
         file->flags |= LFS_F_WRITING;
         return 0;
 
@@ -2541,7 +2561,21 @@ relocate:
     }
 }
 
+static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file) {
+    file->off = file->pos;
+    lfs_alloc_ack(lfs);
+    int err = lfs_file_relocate(lfs, file);
+    if (err) {
+        return err;
+    }
+
+    file->flags &= ~LFS_F_INLINE;
+    return 0;
+}
+
 static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+
     if (file->flags & LFS_F_READING) {
         if (!(file->flags & LFS_F_INLINE)) {
             lfs_cache_drop(lfs, &file->cache);
@@ -2557,7 +2591,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
             lfs_file_t orig = {
                 .ctz.head = file->ctz.head,
                 .ctz.size = file->ctz.size,
-                .flags = LFS_O_RDONLY,
+                .flags = LFS_O_RDONLY | LFS_F_OPENED,
                 .pos = file->pos,
                 .cache = lfs->rcache,
             };
@@ -2621,6 +2655,8 @@ relocate:
 
 int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
     LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file);
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+
     while (true) {
         int err = lfs_file_flush(lfs, file);
         if (err) {
@@ -2674,8 +2710,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
 
 relocate:
         // inline file doesn't fit anymore
-        file->off = file->pos;
-        err = lfs_file_relocate(lfs, file);
+        err = lfs_file_outline(lfs, file);
         if (err) {
             file->flags |= LFS_F_ERRED;
             LFS_TRACE("lfs_file_sync -> %d", err);
@@ -2688,14 +2723,12 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
         void *buffer, lfs_size_t size) {
     LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")",
             (void*)lfs, (void*)file, buffer, size);
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+    LFS_ASSERT((file->flags & 3) != LFS_O_WRONLY);
+
     uint8_t *data = buffer;
     lfs_size_t nsize = size;
 
-    if ((file->flags & 3) == LFS_O_WRONLY) {
-        LFS_TRACE("lfs_file_read -> %"PRId32, LFS_ERR_BADF);
-        return LFS_ERR_BADF;
-    }
-
     if (file->flags & LFS_F_WRITING) {
         // flush out any writes
         int err = lfs_file_flush(lfs, file);
@@ -2770,14 +2803,12 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
         const void *buffer, lfs_size_t size) {
     LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")",
             (void*)lfs, (void*)file, buffer, size);
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+    LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY);
+
     const uint8_t *data = buffer;
     lfs_size_t nsize = size;
 
-    if ((file->flags & 3) == LFS_O_RDONLY) {
-        LFS_TRACE("lfs_file_write -> %"PRId32, LFS_ERR_BADF);
-        return LFS_ERR_BADF;
-    }
-
     if (file->flags & LFS_F_READING) {
         // drop any reads
         int err = lfs_file_flush(lfs, file);
@@ -2816,9 +2847,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
             lfs_min(0x3fe, lfs_min(
                 lfs->cfg->cache_size, lfs->cfg->block_size/8))) {
         // inline file doesn't fit anymore
-        file->off = file->pos;
-        lfs_alloc_ack(lfs);
-        int err = lfs_file_relocate(lfs, file);
+        int err = lfs_file_outline(lfs, file);
         if (err) {
             file->flags |= LFS_F_ERRED;
             LFS_TRACE("lfs_file_write -> %"PRId32, err);
@@ -2905,6 +2934,8 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
         lfs_soff_t off, int whence) {
     LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)",
             (void*)lfs, (void*)file, off, whence);
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+
     // write out everything beforehand, may be noop if rdonly
     int err = lfs_file_flush(lfs, file);
     if (err) {
@@ -2937,10 +2968,8 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
 int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
     LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")",
             (void*)lfs, (void*)file, size);
-    if ((file->flags & 3) == LFS_O_RDONLY) {
-        LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_BADF);
-        return LFS_ERR_BADF;
-    }
+    LFS_ASSERT(file->flags & LFS_F_OPENED);
+    LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY);
 
     if (size > LFS_FILE_MAX) {
         LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_INVAL);
@@ -3003,6 +3032,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
 
 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(file->flags & LFS_F_OPENED);
     (void)lfs;
     LFS_TRACE("lfs_file_tell -> %"PRId32, file->pos);
     return file->pos;
@@ -3022,6 +3052,7 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) {
 
 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(file->flags & LFS_F_OPENED);
     (void)lfs;
     if (file->flags & LFS_F_WRITING) {
         LFS_TRACE("lfs_file_size -> %"PRId32,
@@ -3344,8 +3375,14 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
     LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
             <= lfs->cfg->block_size);
 
-    // we don't support some corner cases
-    LFS_ASSERT(lfs->cfg->block_cycles < 0xffffffff);
+    // block_cycles = 0 is no longer supported.
+    //
+    // block_cycles is the number of erase cycles before littlefs evicts
+    // metadata logs as a part of wear leveling. Suggested values are in the
+    // range of 100-1000, or set block_cycles to -1 to disable block-level
+    // wear-leveling.
+    LFS_ASSERT(lfs->cfg->block_cycles != 0);
+
 
     // setup read cache
     if (lfs->cfg->read_buffer) {
@@ -3493,7 +3530,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
         };
 
         lfs_superblock_tole32(&superblock);
-        err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( 
+        err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
                 {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
                 {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
                 {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
@@ -4533,7 +4570,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
 
                     entry1.d.type &= ~0x80;
                 }
-                
+
                 // also fetch name
                 char name[LFS_NAME_MAX+1];
                 memset(name, 0, sizeof(name));

+ 12 - 7
lfs.h

@@ -136,6 +136,7 @@ enum lfs_open_flags {
     LFS_F_READING = 0x040000, // File has been read since last flush
     LFS_F_ERRED   = 0x080000, // An error occured during write
     LFS_F_INLINE  = 0x100000, // Currently inlined in directory entry
+    LFS_F_OPENED  = 0x200000, // File has been opened
 };
 
 // File seek flags
@@ -190,9 +191,13 @@ struct lfs_config {
     // Number of erasable blocks on the device.
     lfs_size_t block_count;
 
-    // Number of erase cycles before we should move data to another block.
-    // May be zero, in which case no block-level wear-leveling is performed.
-    uint32_t block_cycles;
+    // Number of erase cycles before littlefs evicts metadata logs and moves 
+    // the metadata to another block. Suggested values are in the
+    // range 100-1000, with large values having better performance at the cost
+    // of less consistent wear distribution.
+    //
+    // Set to -1 to disable block-level wear-leveling.
+    int32_t block_cycles;
 
     // Size of block caches. Each cache buffers a portion of a block in RAM.
     // The littlefs needs a read cache, a program cache, and one additional
@@ -204,7 +209,7 @@ 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 4.
+    // can track 8 blocks. Must be a multiple of 8.
     lfs_size_t lookahead_size;
 
     // Optional statically allocated read buffer. Must be cache_size.
@@ -216,7 +221,7 @@ struct lfs_config {
     void *prog_buffer;
 
     // Optional statically allocated lookahead buffer. Must be lookahead_size
-    // and aligned to a 64-bit boundary. By default lfs_malloc is used to
+    // and aligned to a 32-bit boundary. By default lfs_malloc is used to
     // allocate this buffer.
     void *lookahead_buffer;
 
@@ -528,7 +533,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
 // Change the position of the file
 //
 // The change in position is determined by the offset and whence flag.
-// Returns the old position of the file, or a negative error code on failure.
+// Returns the new position of the file, or a negative error code on failure.
 lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
         lfs_soff_t off, int whence);
 
@@ -545,7 +550,7 @@ lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
 
 // Change the position of the file to the beginning of the file
 //
-// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
+// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET)
 // Returns a negative error code on failure.
 int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
 

+ 1 - 1
scripts/template.fmt

@@ -63,7 +63,7 @@ char path[1024];
 #endif
 
 #ifndef LFS_CACHE_SIZE
-#define LFS_CACHE_SIZE 64
+#define LFS_CACHE_SIZE (64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)
 #endif
 
 #ifndef LFS_LOOKAHEAD_SIZE

+ 66 - 7
tests/test_files.sh

@@ -140,20 +140,79 @@ scripts/test.py << TEST
     lfs_unmount(&lfs) => 0;
 TEST
 
-echo "--- Many file test ---"
+echo "--- Many files test ---"
 scripts/test.py << TEST
     lfs_format(&lfs, &cfg) => 0;
 TEST
 scripts/test.py << TEST
-    // Create 300 files of 6 bytes
+    // Create 300 files of 7 bytes
     lfs_mount(&lfs, &cfg) => 0;
-    lfs_mkdir(&lfs, "directory") => 0;
     for (unsigned i = 0; i < 300; i++) {
         sprintf(path, "file_%03d", i);
-        lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
-        lfs_size_t size = 6;
-        memcpy(buffer, "Hello", size);
-        lfs_file_write(&lfs, &file, buffer, size) => size;
+        lfs_file_open(&lfs, &file, path,
+                LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
+        lfs_size_t size = 7;
+        uint8_t wbuffer[1024];
+        uint8_t rbuffer[1024];
+        snprintf((char*)wbuffer, size, "Hi %03d", i);
+        lfs_file_write(&lfs, &file, wbuffer, size) => size;
+        lfs_file_rewind(&lfs, &file) => 0;
+        lfs_file_read(&lfs, &file, rbuffer, size) => size;
+        memcmp(wbuffer, rbuffer, size) => 0;
+        lfs_file_close(&lfs, &file) => 0;
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+
+echo "--- Many files with flush test ---"
+scripts/test.py << TEST
+    lfs_format(&lfs, &cfg) => 0;
+TEST
+scripts/test.py << TEST
+    // Create 300 files of 7 bytes
+    lfs_mount(&lfs, &cfg) => 0;
+    for (unsigned i = 0; i < 300; i++) {
+        sprintf(path, "file_%03d", i);
+        lfs_file_open(&lfs, &file, path,
+                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
+        lfs_size_t size = 7;
+        uint8_t wbuffer[1024];
+        uint8_t rbuffer[1024];
+        snprintf((char*)wbuffer, size, "Hi %03d", i);
+        lfs_file_write(&lfs, &file, wbuffer, size) => size;
+        lfs_file_close(&lfs, &file) => 0;
+
+        lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
+        lfs_file_read(&lfs, &file, rbuffer, size) => size;
+        memcmp(wbuffer, rbuffer, size) => 0;
+        lfs_file_close(&lfs, &file) => 0;
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+
+echo "--- Many files with power cycle test ---"
+scripts/test.py << TEST
+    lfs_format(&lfs, &cfg) => 0;
+TEST
+scripts/test.py << TEST
+    // Create 300 files of 7 bytes
+    lfs_mount(&lfs, &cfg) => 0;
+    for (unsigned i = 0; i < 300; i++) {
+        sprintf(path, "file_%03d", i);
+        lfs_file_open(&lfs, &file, path,
+                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
+        lfs_size_t size = 7;
+        uint8_t wbuffer[1024];
+        uint8_t rbuffer[1024];
+        snprintf((char*)wbuffer, size, "Hi %03d", i);
+        lfs_file_write(&lfs, &file, wbuffer, size) => size;
+        lfs_file_close(&lfs, &file) => 0;
+        lfs_unmount(&lfs) => 0;
+
+        lfs_mount(&lfs, &cfg) => 0;
+        lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
+        lfs_file_read(&lfs, &file, rbuffer, size) => size;
+        memcmp(wbuffer, rbuffer, size) => 0;
         lfs_file_close(&lfs, &file) => 0;
     }
     lfs_unmount(&lfs) => 0;