Browse Source

Merge remote-tracking branch 'origin/master' into v2-rebase-part2

Christopher Haster 7 years ago
parent
commit
c8a39c4b23
10 changed files with 358 additions and 229 deletions
  1. 35 21
      .travis.yml
  2. 2 1
      Makefile
  3. 15 0
      README.md
  4. 2 2
      emubd/lfs_emubd.c
  5. 216 191
      lfs.c
  6. 17 2
      lfs.h
  7. 3 3
      tests/test_alloc.sh
  8. 61 2
      tests/test_dirs.sh
  9. 1 1
      tests/test_seek.sh
  10. 6 6
      tests/test_truncate.sh

+ 35 - 21
.travis.yml

@@ -139,12 +139,15 @@ jobs:
         - LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3)
         - LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3)
         - LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16)))
         - LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16)))
         - LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >>  0)))
         - LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >>  0)))
-        # Grab latests patch from repo tags, default to 0
-        - LFS_VERSION_PATCH=$(curl -f -u "$GEKY_BOT_RELEASES"
-                https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs
-                | jq 'map(.ref | match(
-                    "refs/tags/v'"$LFS_VERSION_MAJOR"'\\.'"$LFS_VERSION_MINOR"'\\.(.*)$")
-                    .captures[].string | tonumber + 1) | max // 0')
+        # Grab latests 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
         # We have our new version
         - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
         - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
         - echo "VERSION $LFS_VERSION"
         - echo "VERSION $LFS_VERSION"
@@ -155,24 +158,35 @@ jobs:
                 | jq -re '.sha')
                 | jq -re '.sha')
           if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ]
           if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ]
           then
           then
-            # Build release notes
-            PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
-            if [ ! -z "$PREV" ]
-            then
-                echo "PREV $PREV"
-                CHANGES=$'### Changes\n\n'$( \
-                    git log --oneline $PREV.. --grep='^Merge' --invert-grep)
-                printf "CHANGES\n%s\n\n" "$CHANGES"
-            fi
-            # Create the release
+            # Create a simple tag
             curl -f -u "$GEKY_BOT_RELEASES" -X POST \
             curl -f -u "$GEKY_BOT_RELEASES" -X POST \
-                https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
+                https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
                 -d "{
                 -d "{
-                    \"tag_name\": \"$LFS_VERSION\",
-                    \"target_commitish\": \"$TRAVIS_COMMIT\",
-                    \"name\": \"${LFS_VERSION%.0}\",
-                    \"body\": $(jq -sR '.' <<< "$CHANGES")
+                    \"ref\": \"refs/tags/$LFS_VERSION\",
+                    \"sha\": \"$TRAVIS_COMMIT\"
                 }"
                 }"
+            # Minor release?
+            if [[ "$LFS_VERSION" == *.0 ]]
+            then
+              # Build release notes
+              PREV=$(git tag --sort=-v:refname -l "v*.0" | head -1)
+              if [ ! -z "$PREV" ]
+              then
+                  echo "PREV $PREV"
+                  CHANGES=$'### Changes\n\n'$( \
+                      git log --oneline $PREV.. --grep='^Merge' --invert-grep)
+                  printf "CHANGES\n%s\n\n" "$CHANGES"
+              fi
+              # Create the release
+              curl -f -u "$GEKY_BOT_RELEASES" -X POST \
+                  https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
+                  -d "{
+                      \"tag_name\": \"$LFS_VERSION\",
+                      \"name\": \"${LFS_VERSION%.0}\",
+                      \"draft\": true,
+                      \"body\": $(jq -sR '.' <<< "$CHANGES")
+                  }"
+            fi
           fi
           fi
 
 
 # Manage statuses
 # Manage statuses

+ 2 - 1
Makefile

@@ -25,7 +25,8 @@ ifdef WORD
 override CFLAGS += -m$(WORD)
 override CFLAGS += -m$(WORD)
 endif
 endif
 override CFLAGS += -I.
 override CFLAGS += -I.
-override CFLAGS += -std=c99 -Wall -pedantic -Wshadow -Wunused-parameter
+override CFLAGS += -std=c99 -Wall -pedantic
+override CFLAGS += -Wshadow -Wunused-parameter -Wjump-misses-init -Wsign-compare
 
 
 
 
 all: $(TARGET)
 all: $(TARGET)

+ 15 - 0
README.md

@@ -176,3 +176,18 @@ handy.
 [littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for
 [littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for
 littlefs. I'm not sure why you would want this, but it is handy for demos.
 littlefs. I'm not sure why you would want this, but it is handy for demos.
 You can see it in action [here](http://littlefs.geky.net/demo.html).
 You can see it in action [here](http://littlefs.geky.net/demo.html).
+
+[mklfs](https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src) -
+A command line tool built by the [Lua RTOS](https://github.com/whitecatboard/Lua-RTOS-ESP32)
+guys for making littlefs images from a host PC. Supports Windows, Mac OS,
+and Linux.
+
+[SPIFFS](https://github.com/pellepl/spiffs) - Another excellent embedded
+filesystem for NOR flash. As a more traditional logging filesystem with full
+static wear-leveling, SPIFFS will likely outperform littlefs on small
+memories such as the internal flash on microcontrollers.
+
+[Dhara](https://github.com/dlbeer/dhara) - An interesting NAND flash
+translation layer designed for small MCUs. It offers static wear-leveling and
+power-resilience with only a fixed O(|address|) pointer structure stored on
+each block and in RAM.

+ 2 - 2
emubd/lfs_emubd.c

@@ -30,7 +30,7 @@ static inline void lfs_emubd_tole32(lfs_emubd_t *emu) {
     emu->stats.prog_count  = lfs_tole32(emu->stats.prog_count);
     emu->stats.prog_count  = lfs_tole32(emu->stats.prog_count);
     emu->stats.erase_count = lfs_tole32(emu->stats.erase_count);
     emu->stats.erase_count = lfs_tole32(emu->stats.erase_count);
 
 
-    for (int i = 0; i < sizeof(emu->history.blocks) /
+    for (unsigned i = 0; i < sizeof(emu->history.blocks) /
             sizeof(emu->history.blocks[0]); i++) {
             sizeof(emu->history.blocks[0]); i++) {
         emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]);
         emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]);
     }
     }
@@ -46,7 +46,7 @@ static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) {
     emu->stats.prog_count  = lfs_fromle32(emu->stats.prog_count);
     emu->stats.prog_count  = lfs_fromle32(emu->stats.prog_count);
     emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count);
     emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count);
 
 
-    for (int i = 0; i < sizeof(emu->history.blocks) /
+    for (unsigned i = 0; i < sizeof(emu->history.blocks) /
             sizeof(emu->history.blocks[0]); i++) {
             sizeof(emu->history.blocks[0]); i++) {
         emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]);
         emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]);
     }
     }

+ 216 - 191
lfs.c

@@ -322,14 +322,14 @@ static inline void lfs_global_xor(struct lfs_globals *a,
         const struct lfs_globals *b) {
         const struct lfs_globals *b) {
     uint32_t *a32 = (uint32_t *)a;
     uint32_t *a32 = (uint32_t *)a;
     const uint32_t *b32 = (const uint32_t *)b;
     const uint32_t *b32 = (const uint32_t *)b;
-    for (int i = 0; i < sizeof(struct lfs_globals)/4; i++) {
+    for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) {
         a32[i] ^= b32[i];
         a32[i] ^= b32[i];
     }
     }
 }
 }
 
 
 static inline bool lfs_global_iszero(const struct lfs_globals *a) {
 static inline bool lfs_global_iszero(const struct lfs_globals *a) {
     const uint32_t *a32 = (const uint32_t *)a;
     const uint32_t *a32 = (const uint32_t *)a;
-    for (int i = 0; i < sizeof(struct lfs_globals)/4; i++) {
+    for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) {
         if (a32[i] != 0) {
         if (a32[i] != 0) {
             return false;
             return false;
         }
         }
@@ -387,6 +387,7 @@ static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) {
     superblock->name_max    = lfs_fromle32(superblock->name_max);
     superblock->name_max    = lfs_fromle32(superblock->name_max);
     superblock->inline_max  = lfs_fromle32(superblock->inline_max);
     superblock->inline_max  = lfs_fromle32(superblock->inline_max);
     superblock->attr_max    = lfs_fromle32(superblock->attr_max);
     superblock->attr_max    = lfs_fromle32(superblock->attr_max);
+    superblock->file_max    = lfs_fromle32(superblock->file_max);
 }
 }
 
 
 static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
 static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
@@ -396,6 +397,7 @@ static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
     superblock->name_max    = lfs_tole32(superblock->name_max);
     superblock->name_max    = lfs_tole32(superblock->name_max);
     superblock->inline_max  = lfs_tole32(superblock->inline_max);
     superblock->inline_max  = lfs_tole32(superblock->inline_max);
     superblock->attr_max    = lfs_tole32(superblock->attr_max);
     superblock->attr_max    = lfs_tole32(superblock->attr_max);
+    superblock->file_max    = lfs_tole32(superblock->file_max);
 }
 }
 
 
 
 
@@ -1347,13 +1349,13 @@ commit:
                         (const lfs_block_t[2]){0, 1}) == 0) {
                         (const lfs_block_t[2]){0, 1}) == 0) {
                     // we're writing too much to the superblock,
                     // we're writing too much to the superblock,
                     // should we expand?
                     // should we expand?
-                    lfs_stag_t res = lfs_fs_size(lfs);
+                    lfs_ssize_t res = lfs_fs_size(lfs);
                     if (res < 0) {
                     if (res < 0) {
                         return res;
                         return res;
                     }
                     }
 
 
                     // do we have enough space to expand?
                     // do we have enough space to expand?
-                    if (res < lfs->cfg->block_count/2) {
+                    if ((lfs_size_t)res < lfs->cfg->block_count/2) {
                         LFS_DEBUG("Expanding superblock at rev %"PRIu32,
                         LFS_DEBUG("Expanding superblock at rev %"PRIu32,
                                 dir->rev);
                                 dir->rev);
                         exhausted = true;
                         exhausted = true;
@@ -1376,93 +1378,95 @@ commit:
             }
             }
         }
         }
 
 
-        // write out header
-        uint32_t rev = lfs_tole32(dir->rev);
-        err = lfs_commit_prog(lfs, &commit, &rev, sizeof(rev));
-        if (err) {
-            if (err == LFS_ERR_CORRUPT) {
-                goto relocate;
+        if (true) {
+            // write out header
+            uint32_t rev = lfs_tole32(dir->rev);
+            err = lfs_commit_prog(lfs, &commit, &rev, sizeof(rev));
+            if (err) {
+                if (err == LFS_ERR_CORRUPT) {
+                    goto relocate;
+                }
+                return err;
             }
             }
-            return err;
-        }
 
 
-        // commit with a move
-        for (uint16_t id = begin; id < end || commit.off < commit.ack; id++) {
-            for (int pass = 0; pass < 2; pass++) {
-                err = lfs_commit_move(lfs, &commit, pass,
-                        0x003fe000, LFS_MKTAG(0, id, 0),
-                        -LFS_MKTAG(0, begin, 0),
-                        source, attrs);
-                if (err && !(splitted && !overcompacting &&
-                        err == LFS_ERR_NOSPC)) {
-                    if (!overcompacting && err == LFS_ERR_NOSPC) {
-                        goto split;
-                    } else if (err == LFS_ERR_CORRUPT) {
-                        goto relocate;
+            // commit with a move
+            for (uint16_t id = begin; id < end || commit.off < commit.ack; id++) {
+                for (int pass = 0; pass < 2; pass++) {
+                    err = lfs_commit_move(lfs, &commit, pass,
+                            0x003fe000, LFS_MKTAG(0, id, 0),
+                            -LFS_MKTAG(0, begin, 0),
+                            source, attrs);
+                    if (err && !(splitted && !overcompacting &&
+                            err == LFS_ERR_NOSPC)) {
+                        if (!overcompacting && err == LFS_ERR_NOSPC) {
+                            goto split;
+                        } else if (err == LFS_ERR_CORRUPT) {
+                            goto relocate;
+                        }
+                        return err;
                     }
                     }
-                    return err;
                 }
                 }
+
+                ackid = id;
             }
             }
 
 
-            ackid = id;
-        }
+            // reopen reserved space at the end
+            commit.end = lfs->cfg->block_size - 8;
 
 
-        // reopen reserved space at the end
-        commit.end = lfs->cfg->block_size - 8;
+            if (ackid >= end) {
+                // extra garbage attributes were written out during split,
+                // need to clean up
+                err = lfs_commit_attr(lfs, &commit,
+                        LFS_MKTAG(LFS_TYPE_DELETE, ackid, 0), NULL);
+                if (err) {
+                    if (err == LFS_ERR_CORRUPT) {
+                        goto relocate;
+                    }
+                    return err;
+                }
+            }
 
 
-        if (ackid >= end) {
-            // extra garbage attributes were written out during split,
-            // need to clean up
-            err = lfs_commit_attr(lfs, &commit,
-                    LFS_MKTAG(LFS_TYPE_DELETE, ackid, 0), NULL);
-            if (err) {
-                if (err == LFS_ERR_CORRUPT) {
-                    goto relocate;
+            if (!relocated && !lfs_global_iszero(&lfs->locals)) {
+                // commit any globals, unless we're relocating,
+                // in which case our parent will steal our globals
+                err = lfs_commit_globals(lfs, &commit, &lfs->locals);
+                if (err) {
+                    if (err == LFS_ERR_CORRUPT) {
+                        goto relocate;
+                    }
+                    return err;
                 }
                 }
-                return err;
             }
             }
-        }
 
 
-        if (!relocated && !lfs_global_iszero(&lfs->locals)) {
-            // commit any globals, unless we're relocating,
-            // in which case our parent will steal our globals
-            err = lfs_commit_globals(lfs, &commit, &lfs->locals);
-            if (err) {
-                if (err == LFS_ERR_CORRUPT) {
-                    goto relocate;
+            if (!lfs_pair_isnull(dir->tail)) {
+                // commit tail, which may be new after last size check
+                lfs_pair_tole32(dir->tail);
+                err = lfs_commit_attr(lfs, &commit,
+                        LFS_MKTAG(LFS_TYPE_TAIL + dir->split,
+                            0x1ff, sizeof(dir->tail)), dir->tail);
+                lfs_pair_fromle32(dir->tail);
+                if (err) {
+                    if (err == LFS_ERR_CORRUPT) {
+                        goto relocate;
+                    }
+                    return err;
                 }
                 }
-                return err;
             }
             }
-        }
 
 
-        if (!lfs_pair_isnull(dir->tail)) {
-            // commit tail, which may be new after last size check
-            lfs_pair_tole32(dir->tail);
-            err = lfs_commit_attr(lfs, &commit,
-                    LFS_MKTAG(LFS_TYPE_TAIL + dir->split,
-                        0x1ff, sizeof(dir->tail)), dir->tail);
-            lfs_pair_fromle32(dir->tail);
+            err = lfs_commit_crc(lfs, &commit, true);
             if (err) {
             if (err) {
                 if (err == LFS_ERR_CORRUPT) {
                 if (err == LFS_ERR_CORRUPT) {
                     goto relocate;
                     goto relocate;
                 }
                 }
                 return err;
                 return err;
             }
             }
-        }
 
 
-        err = lfs_commit_crc(lfs, &commit, true);
-        if (err) {
-            if (err == LFS_ERR_CORRUPT) {
-                goto relocate;
-            }
-            return err;
+            // successful compaction, swap dir pair to indicate most recent
+            lfs_pair_swap(dir->pair);
+            dir->off = commit.off;
+            dir->etag = commit.ptag;
+            dir->erased = true;
         }
         }
-
-        // successful compaction, swap dir pair to indicate most recent
-        lfs_pair_swap(dir->pair);
-        dir->off = commit.off;
-        dir->etag = commit.ptag;
-        dir->erased = true;
         break;
         break;
 
 
 split:
 split:
@@ -1560,7 +1564,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
         // Wait, we have the move? Just cancel this out here
         // Wait, we have the move? Just cancel this out here
         // We need to, or else the move can become outdated
         // We need to, or else the move can become outdated
         cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.id, 0);
         cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.id, 0);
-        cancelattr.next = attrs;
+        cancelattr.next = attrs; // TODO need order
         attrs = &cancelattr;
         attrs = &cancelattr;
 
 
         cancels.hasmove = lfs->globals.hasmove;
         cancels.hasmove = lfs->globals.hasmove;
@@ -1602,11 +1606,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
         attrcount += 1;
         attrcount += 1;
     }
     }
 
 
-    while (true) {
-        if (!dir->erased) {
-            goto compact;
-        }
-
+    if (dir->erased) {
         // try to commit
         // try to commit
         struct lfs_commit commit = {
         struct lfs_commit commit = {
             .block = dir->pair[0],
             .block = dir->pair[0],
@@ -1668,18 +1668,15 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
         dir->etag = commit.ptag;
         dir->etag = commit.ptag;
         // successful commit, update globals
         // successful commit, update globals
         lfs_global_zero(&lfs->locals);
         lfs_global_zero(&lfs->locals);
-        break;
-
+    } else {
 compact:
 compact:
         // fall back to compaction
         // fall back to compaction
         lfs_cache_drop(lfs, &lfs->pcache);
         lfs_cache_drop(lfs, &lfs->pcache);
 
 
-        err = lfs_dir_compact(lfs, dir, attrs, dir, 0, dir->count);
+        int err = lfs_dir_compact(lfs, dir, attrs, dir, 0, dir->count);
         if (err) {
         if (err) {
             return err;
             return err;
         }
         }
-
-        break;
     }
     }
 
 
     // update globals that are affected
     // update globals that are affected
@@ -2021,84 +2018,86 @@ static int lfs_ctz_extend(lfs_t *lfs,
         }
         }
         LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count);
         LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count);
 
 
-        err = lfs_bd_erase(lfs, nblock);
-        if (err) {
-            if (err == LFS_ERR_CORRUPT) {
-                goto relocate;
+        if (true) {
+            err = lfs_bd_erase(lfs, nblock);
+            if (err) {
+                if (err == LFS_ERR_CORRUPT) {
+                    goto relocate;
+                }
+                return err;
             }
             }
-            return err;
-        }
 
 
-        if (size == 0) {
-            *block = nblock;
-            *off = 0;
-            return 0;
-        }
+            if (size == 0) {
+                *block = nblock;
+                *off = 0;
+                return 0;
+            }
 
 
-        size -= 1;
-        lfs_off_t index = lfs_ctz_index(lfs, &size);
-        size += 1;
+            size -= 1;
+            lfs_off_t index = lfs_ctz_index(lfs, &size);
+            size += 1;
 
 
-        // just copy out the last block if it is incomplete
-        if (size != lfs->cfg->block_size) {
-            for (lfs_off_t i = 0; i < size; i++) {
-                uint8_t data;
-                err = lfs_bd_read(lfs,
-                        NULL, rcache, size-i,
-                        head, i, &data, 1);
-                if (err) {
-                    return err;
+            // just copy out the last block if it is incomplete
+            if (size != lfs->cfg->block_size) {
+                for (lfs_off_t i = 0; i < size; i++) {
+                    uint8_t data;
+                    err = lfs_bd_read(lfs,
+                            NULL, rcache, size-i,
+                            head, i, &data, 1);
+                    if (err) {
+                        return err;
+                    }
+
+                    err = lfs_bd_prog(lfs,
+                            pcache, rcache, true,
+                            nblock, i, &data, 1);
+                    if (err) {
+                        if (err == LFS_ERR_CORRUPT) {
+                            goto relocate;
+                        }
+                        return err;
+                    }
                 }
                 }
 
 
-                err = lfs_bd_prog(lfs,
-                        pcache, rcache, true,
-                        nblock, i, &data, 1);
+                *block = nblock;
+                *off = size;
+                return 0;
+            }
+
+            // append block
+            index += 1;
+            lfs_size_t skips = lfs_ctz(index) + 1;
+
+            for (lfs_off_t i = 0; i < skips; i++) {
+                head = lfs_tole32(head);
+                err = lfs_bd_prog(lfs, pcache, rcache, true,
+                        nblock, 4*i, &head, 4);
+                head = lfs_fromle32(head);
                 if (err) {
                 if (err) {
                     if (err == LFS_ERR_CORRUPT) {
                     if (err == LFS_ERR_CORRUPT) {
                         goto relocate;
                         goto relocate;
                     }
                     }
                     return err;
                     return err;
                 }
                 }
-            }
-
-            *block = nblock;
-            *off = size;
-            return 0;
-        }
 
 
-        // append block
-        index += 1;
-        lfs_size_t skips = lfs_ctz(index) + 1;
-
-        for (lfs_off_t i = 0; i < skips; i++) {
-            head = lfs_tole32(head);
-            err = lfs_bd_prog(lfs, pcache, rcache, true,
-                    nblock, 4*i, &head, 4);
-            head = lfs_fromle32(head);
-            if (err) {
-                if (err == LFS_ERR_CORRUPT) {
-                    goto relocate;
+                if (i != skips-1) {
+                    err = lfs_bd_read(lfs,
+                            NULL, rcache, sizeof(head),
+                            head, 4*i, &head, sizeof(head));
+                    head = lfs_fromle32(head);
+                    if (err) {
+                        return err;
+                    }
                 }
                 }
-                return err;
-            }
 
 
-            if (i != skips-1) {
-                err = lfs_bd_read(lfs,
-                        NULL, rcache, sizeof(head),
-                        head, 4*i, &head, sizeof(head));
-                head = lfs_fromle32(head);
-                if (err) {
-                    return err;
-                }
+                LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
             }
             }
 
 
-            LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
+            *block = nblock;
+            *off = 4*skips;
+            return 0;
         }
         }
 
 
-        *block = nblock;
-        *off = 4*skips;
-        return 0;
-
 relocate:
 relocate:
         LFS_DEBUG("Bad block at %"PRIu32, nblock);
         LFS_DEBUG("Bad block at %"PRIu32, nblock);
 
 
@@ -2596,6 +2595,11 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
         file->pos = file->ctz.size;
         file->pos = file->ctz.size;
     }
     }
 
 
+    if (file->pos + size > lfs->file_max) {
+        // Larger than file limit?
+        return LFS_ERR_FBIG;
+    }
+
     if (!(file->flags & LFS_F_WRITING) && file->pos > file->ctz.size) {
     if (!(file->flags & LFS_F_WRITING) && file->pos > file->ctz.size) {
         // fill with zeros
         // fill with zeros
         lfs_off_t pos = file->pos;
         lfs_off_t pos = file->pos;
@@ -2704,24 +2708,24 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
         return err;
         return err;
     }
     }
 
 
-    // update pos
+    // find new pos
+    lfs_off_t npos = file->pos;
     if (whence == LFS_SEEK_SET) {
     if (whence == LFS_SEEK_SET) {
-        file->pos = off;
+        npos = off;
     } else if (whence == LFS_SEEK_CUR) {
     } else if (whence == LFS_SEEK_CUR) {
-        if (off < 0 && (lfs_off_t)-off > file->pos) {
-            return LFS_ERR_INVAL;
-        }
-
-        file->pos = file->pos + off;
+        npos = file->pos + off;
     } else if (whence == LFS_SEEK_END) {
     } else if (whence == LFS_SEEK_END) {
-        if (off < 0 && (lfs_off_t)-off > file->ctz.size) {
-            return LFS_ERR_INVAL;
-        }
+        npos = file->ctz.size + off;
+    }
 
 
-        file->pos = file->ctz.size + off;
+    if (npos < 0 || npos > lfs->file_max) {
+        // file position out of range
+        return LFS_ERR_INVAL;
     }
     }
 
 
-    return file->pos;
+    // update pos
+    file->pos = npos;
+    return npos;
 }
 }
 
 
 int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
 int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
@@ -3112,6 +3116,12 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
         lfs->attr_max = LFS_ATTR_MAX;
         lfs->attr_max = LFS_ATTR_MAX;
     }
     }
 
 
+    LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX);
+    lfs->file_max = lfs->cfg->file_max;
+    if (!lfs->file_max) {
+        lfs->file_max = LFS_FILE_MAX;
+    }
+
     // setup default state
     // setup default state
     lfs->root[0] = 0xffffffff;
     lfs->root[0] = 0xffffffff;
     lfs->root[1] = 0xffffffff;
     lfs->root[1] = 0xffffffff;
@@ -3145,50 +3155,54 @@ static int lfs_deinit(lfs_t *lfs) {
 }
 }
 
 
 int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
 int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
-    int err = lfs_init(lfs, cfg);
-    if (err) {
-        return err;
-    }
+    int err = 0;
+    if (true) {
+        err = lfs_init(lfs, cfg);
+        if (err) {
+            return err;
+        }
 
 
-    // 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,
-            lfs->cfg->block_count);
-    lfs->free.i = 0;
-    lfs_alloc_ack(lfs);
+        // 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,
+                lfs->cfg->block_count);
+        lfs->free.i = 0;
+        lfs_alloc_ack(lfs);
 
 
-    // create root dir
-    lfs_mdir_t root;
-    err = lfs_dir_alloc(lfs, &root);
-    if (err) {
-        goto cleanup;
-    }
+        // create root dir
+        lfs_mdir_t root;
+        err = lfs_dir_alloc(lfs, &root);
+        if (err) {
+            goto cleanup;
+        }
 
 
-    // write one superblock
-    lfs_superblock_t superblock = {
-        .version     = LFS_DISK_VERSION,
-        .block_size  = lfs->cfg->block_size,
-        .block_count = lfs->cfg->block_count,
-        .name_max    = lfs->name_max,
-        .inline_max  = lfs->inline_max,
-        .attr_max    = lfs->attr_max,
-    };
-
-    lfs_superblock_tole32(&superblock);
-    err = lfs_dir_commit(lfs, &root,
-            LFS_MKATTR(LFS_TYPE_INLINESTRUCT, 0,
-                &superblock, sizeof(superblock),
-            LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, "littlefs", 8,
-            NULL)));
-    if (err) {
-        goto cleanup;
-    }
+        // write one superblock
+        lfs_superblock_t superblock = {
+            .version     = LFS_DISK_VERSION,
+            .block_size  = lfs->cfg->block_size,
+            .block_count = lfs->cfg->block_count,
+            .name_max    = lfs->name_max,
+            .inline_max  = lfs->inline_max,
+            .attr_max    = lfs->attr_max,
+            .file_max    = lfs->file_max,
+        };
 
 
-    // sanity check that fetch works
-    err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1});
-    if (err) {
-        goto cleanup;
+        lfs_superblock_tole32(&superblock);
+        err = lfs_dir_commit(lfs, &root,
+                LFS_MKATTR(LFS_TYPE_INLINESTRUCT, 0,
+                    &superblock, sizeof(superblock),
+                LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, "littlefs", 8,
+                NULL)));
+        if (err) {
+            goto cleanup;
+        }
+
+        // sanity check that fetch works
+        err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1});
+        if (err) {
+            goto cleanup;
+        }
     }
     }
 
 
 cleanup:
 cleanup:
@@ -3276,6 +3290,17 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
 
 
                 lfs->attr_max = superblock.attr_max;
                 lfs->attr_max = superblock.attr_max;
             }
             }
+
+            if (superblock.file_max) {
+                if (superblock.file_max > lfs->file_max) {
+                    LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")",
+                            superblock.file_max, lfs->file_max);
+                    err = LFS_ERR_INVAL;
+                    goto cleanup;
+                }
+
+                lfs->file_max = superblock.file_max;
+            }
         }
         }
 
 
         // has globals?
         // has globals?

+ 17 - 2
lfs.h

@@ -66,6 +66,15 @@ typedef uint32_t lfs_block_t;
 #define LFS_ATTR_MAX 0x1ffe
 #define LFS_ATTR_MAX 0x1ffe
 #endif
 #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 signed sizes. Stored in superblock and must be
+// respected by other littlefs drivers.
+#ifndef LFS_FILE_MAX
+#define LFS_FILE_MAX 2147483647
+#endif
+
 // Possible error codes, these are negative to allow
 // Possible error codes, these are negative to allow
 // valid positive return values
 // valid positive return values
 enum lfs_error {
 enum lfs_error {
@@ -78,6 +87,7 @@ enum lfs_error {
     LFS_ERR_ISDIR       = -21,  // Entry is a dir
     LFS_ERR_ISDIR       = -21,  // Entry is a dir
     LFS_ERR_NOTEMPTY    = -39,  // Dir is not empty
     LFS_ERR_NOTEMPTY    = -39,  // Dir is not empty
     LFS_ERR_BADF        = -9,   // Bad file number
     LFS_ERR_BADF        = -9,   // Bad file number
+    LFS_ERR_FBIG        = -27,  // File too large
     LFS_ERR_INVAL       = -22,  // Invalid parameter
     LFS_ERR_INVAL       = -22,  // Invalid parameter
     LFS_ERR_NOSPC       = -28,  // No space left on device
     LFS_ERR_NOSPC       = -28,  // No space left on device
     LFS_ERR_NOMEM       = -12,  // No more memory available
     LFS_ERR_NOMEM       = -12,  // No more memory available
@@ -233,6 +243,11 @@ struct lfs_config {
     // LFS_ATTR_MAX when zero. Stored in superblock and must be respected by
     // LFS_ATTR_MAX when zero. Stored in superblock and must be respected by
     // other littlefs drivers.
     // other littlefs drivers.
     lfs_size_t attr_max;
     lfs_size_t attr_max;
+
+    // Optional upper limit on files in bytes. No downside for larger files
+    // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
+    // in superblock and must be respected by other littlefs drivers.
+    lfs_size_t file_max;
 };
 };
 
 
 // File info structure
 // File info structure
@@ -346,6 +361,7 @@ typedef struct lfs_superblock {
     lfs_size_t name_max;
     lfs_size_t name_max;
     lfs_size_t inline_max;
     lfs_size_t inline_max;
     lfs_size_t attr_max;
     lfs_size_t attr_max;
+    lfs_size_t file_max;
 } lfs_superblock_t;
 } lfs_superblock_t;
 
 
 // The littlefs filesystem type
 // The littlefs filesystem type
@@ -378,11 +394,10 @@ typedef struct lfs {
     } free;
     } free;
 
 
     const struct lfs_config *cfg;
     const struct lfs_config *cfg;
-    lfs_size_t block_size;
-    lfs_size_t block_count;
     lfs_size_t name_max;
     lfs_size_t name_max;
     lfs_size_t inline_max;
     lfs_size_t inline_max;
     lfs_size_t attr_max;
     lfs_size_t attr_max;
+    lfs_size_t file_max;
 } lfs_t;
 } lfs_t;
 
 
 
 

+ 3 - 3
tests/test_alloc.sh

@@ -32,18 +32,18 @@ lfs_alloc_singleproc() {
 tests/test.py << TEST
 tests/test.py << TEST
     const char *names[] = {"bacon", "eggs", "pancakes"};
     const char *names[] = {"bacon", "eggs", "pancakes"};
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
-    for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
+    for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
         sprintf((char*)buffer, "$1/%s", names[n]);
         sprintf((char*)buffer, "$1/%s", names[n]);
         lfs_file_open(&lfs, &file[n], (char*)buffer,
         lfs_file_open(&lfs, &file[n], (char*)buffer,
                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
     }
     }
-    for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
+    for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
         size = strlen(names[n]);
         size = strlen(names[n]);
         for (int i = 0; i < $SIZE; i++) {
         for (int i = 0; i < $SIZE; i++) {
             lfs_file_write(&lfs, &file[n], names[n], size) => size;
             lfs_file_write(&lfs, &file[n], names[n], size) => size;
         }
         }
     }
     }
-    for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
+    for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
         lfs_file_close(&lfs, &file[n]) => 0;
         lfs_file_close(&lfs, &file[n]) => 0;
     }
     }
     lfs_unmount(&lfs) => 0;
     lfs_unmount(&lfs) => 0;

+ 61 - 2
tests/test_dirs.sh

@@ -326,13 +326,42 @@ tests/test.py << TEST
     lfs_unmount(&lfs) => 0;
     lfs_unmount(&lfs) => 0;
 TEST
 TEST
 
 
+echo "--- Multi-block rename ---"
+tests/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    for (int i = 0; i < $LARGESIZE; i++) {
+        sprintf((char*)buffer, "cactus/test%03d", i);
+        sprintf((char*)wbuffer, "cactus/tedd%03d", i);
+        lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0;
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+tests/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    lfs_dir_open(&lfs, &dir[0], "cactus") => 0;
+    lfs_dir_read(&lfs, &dir[0], &info) => 1;
+    strcmp(info.name, ".") => 0;
+    info.type => LFS_TYPE_DIR;
+    lfs_dir_read(&lfs, &dir[0], &info) => 1;
+    strcmp(info.name, "..") => 0;
+    info.type => LFS_TYPE_DIR;
+    for (int i = 0; i < $LARGESIZE; i++) {
+        sprintf((char*)buffer, "tedd%03d", i);
+        lfs_dir_read(&lfs, &dir[0], &info) => 1;
+        strcmp(info.name, (char*)buffer) => 0;
+        info.type => LFS_TYPE_DIR;
+    }
+    lfs_dir_read(&lfs, &dir[0], &info) => 0;
+    lfs_unmount(&lfs) => 0;
+TEST
+
 echo "--- Multi-block remove ---"
 echo "--- Multi-block remove ---"
 tests/test.py << TEST
 tests/test.py << TEST
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
     lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
     lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
 
 
     for (int i = 0; i < $LARGESIZE; i++) {
     for (int i = 0; i < $LARGESIZE; i++) {
-        sprintf((char*)buffer, "cactus/test%03d", i);
+        sprintf((char*)buffer, "cactus/tedd%03d", i);
         lfs_remove(&lfs, (char*)buffer) => 0;
         lfs_remove(&lfs, (char*)buffer) => 0;
     }
     }
 
 
@@ -391,13 +420,43 @@ tests/test.py << TEST
     lfs_unmount(&lfs) => 0;
     lfs_unmount(&lfs) => 0;
 TEST
 TEST
 
 
+echo "--- Multi-block rename with files ---"
+tests/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    for (int i = 0; i < $LARGESIZE; i++) {
+        sprintf((char*)buffer, "prickly-pear/test%03d", i);
+        sprintf((char*)wbuffer, "prickly-pear/tedd%03d", i);
+        lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0;
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+tests/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    lfs_dir_open(&lfs, &dir[0], "prickly-pear") => 0;
+    lfs_dir_read(&lfs, &dir[0], &info) => 1;
+    strcmp(info.name, ".") => 0;
+    info.type => LFS_TYPE_DIR;
+    lfs_dir_read(&lfs, &dir[0], &info) => 1;
+    strcmp(info.name, "..") => 0;
+    info.type => LFS_TYPE_DIR;
+    for (int i = 0; i < $LARGESIZE; i++) {
+        sprintf((char*)buffer, "tedd%03d", i);
+        lfs_dir_read(&lfs, &dir[0], &info) => 1;
+        strcmp(info.name, (char*)buffer) => 0;
+        info.type => LFS_TYPE_REG;
+        info.size => 6;
+    }
+    lfs_dir_read(&lfs, &dir[0], &info) => 0;
+    lfs_unmount(&lfs) => 0;
+TEST
+
 echo "--- Multi-block remove with files ---"
 echo "--- Multi-block remove with files ---"
 tests/test.py << TEST
 tests/test.py << TEST
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
     lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
     lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
 
 
     for (int i = 0; i < $LARGESIZE; i++) {
     for (int i = 0; i < $LARGESIZE; i++) {
-        sprintf((char*)buffer, "prickly-pear/test%03d", i);
+        sprintf((char*)buffer, "prickly-pear/tedd%03d", i);
         lfs_remove(&lfs, (char*)buffer) => 0;
         lfs_remove(&lfs, (char*)buffer) => 0;
     }
     }
 
 

+ 1 - 1
tests/test_seek.sh

@@ -301,7 +301,7 @@ tests/test.py << TEST
     size = strlen("hedgehoghog");
     size = strlen("hedgehoghog");
     const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
     const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
 
 
-    for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
+    for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
         lfs_soff_t off = offsets[i];
         lfs_soff_t off = offsets[i];
         memcpy(buffer, "hedgehoghog", size);
         memcpy(buffer, "hedgehoghog", size);
         lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off;
         lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off;

+ 6 - 6
tests/test_truncate.sh

@@ -23,14 +23,14 @@ tests/test.py << TEST
 
 
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
 
 
-    for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
+    for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
         sprintf((char*)buffer, "hairyhead%d", i);
         sprintf((char*)buffer, "hairyhead%d", i);
         lfs_file_open(&lfs, &file[0], (const char*)buffer,
         lfs_file_open(&lfs, &file[0], (const char*)buffer,
                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
 
 
         strcpy((char*)buffer, "hair");
         strcpy((char*)buffer, "hair");
         size = strlen((char*)buffer);
         size = strlen((char*)buffer);
-        for (int j = 0; j < startsizes[i]; j += size) {
+        for (lfs_off_t j = 0; j < startsizes[i]; j += size) {
             lfs_file_write(&lfs, &file[0], buffer, size) => size;
             lfs_file_write(&lfs, &file[0], buffer, size) => size;
         }
         }
         lfs_file_size(&lfs, &file[0]) => startsizes[i];
         lfs_file_size(&lfs, &file[0]) => startsizes[i];
@@ -55,13 +55,13 @@ tests/test.py << TEST
 
 
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
 
 
-    for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
+    for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
         sprintf((char*)buffer, "hairyhead%d", i);
         sprintf((char*)buffer, "hairyhead%d", i);
         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0;
         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0;
         lfs_file_size(&lfs, &file[0]) => hotsizes[i];
         lfs_file_size(&lfs, &file[0]) => hotsizes[i];
 
 
         size = strlen("hair");
         size = strlen("hair");
-        int j = 0;
+        lfs_off_t j = 0;
         for (; j < startsizes[i] && j < hotsizes[i]; j += size) {
         for (; j < startsizes[i] && j < hotsizes[i]; j += size) {
             lfs_file_read(&lfs, &file[0], buffer, size) => size;
             lfs_file_read(&lfs, &file[0], buffer, size) => size;
             memcmp(buffer, "hair", size) => 0;
             memcmp(buffer, "hair", size) => 0;
@@ -87,13 +87,13 @@ tests/test.py << TEST
 
 
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
 
 
-    for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
+    for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
         sprintf((char*)buffer, "hairyhead%d", i);
         sprintf((char*)buffer, "hairyhead%d", i);
         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0;
         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0;
         lfs_file_size(&lfs, &file[0]) => coldsizes[i];
         lfs_file_size(&lfs, &file[0]) => coldsizes[i];
 
 
         size = strlen("hair");
         size = strlen("hair");
-        int j = 0;
+        lfs_off_t j = 0;
         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i];
         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i];
                 j += size) {
                 j += size) {
             lfs_file_read(&lfs, &file[0], buffer, size) => size;
             lfs_file_read(&lfs, &file[0], buffer, size) => size;