Ver Fonte

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

Christopher Haster há 7 anos atrás
pai
commit
c8a39c4b23
10 ficheiros alterados com 358 adições e 229 exclusões
  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_MAJOR=$((0xffff & ($LFS_VERSION >> 16)))
         - 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
         - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
         - echo "VERSION $LFS_VERSION"
@@ -155,24 +158,35 @@ jobs:
                 | jq -re '.sha')
           if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ]
           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 \
-                https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
+                https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
                 -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
 
 # Manage statuses

+ 2 - 1
Makefile

@@ -25,7 +25,8 @@ ifdef WORD
 override CFLAGS += -m$(WORD)
 endif
 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)

+ 15 - 0
README.md

@@ -176,3 +176,18 @@ handy.
 [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.
 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.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++) {
         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.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++) {
         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) {
     uint32_t *a32 = (uint32_t *)a;
     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];
     }
 }
 
 static inline bool lfs_global_iszero(const struct lfs_globals *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) {
             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->inline_max  = lfs_fromle32(superblock->inline_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) {
@@ -396,6 +397,7 @@ static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
     superblock->name_max    = lfs_tole32(superblock->name_max);
     superblock->inline_max  = lfs_tole32(superblock->inline_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) {
                     // we're writing too much to the superblock,
                     // should we expand?
-                    lfs_stag_t res = lfs_fs_size(lfs);
+                    lfs_ssize_t res = lfs_fs_size(lfs);
                     if (res < 0) {
                         return res;
                     }
 
                     // 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,
                                 dir->rev);
                         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 == LFS_ERR_CORRUPT) {
                     goto relocate;
                 }
                 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;
 
 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
         // We need to, or else the move can become outdated
         cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.id, 0);
-        cancelattr.next = attrs;
+        cancelattr.next = attrs; // TODO need order
         attrs = &cancelattr;
 
         cancels.hasmove = lfs->globals.hasmove;
@@ -1602,11 +1606,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
         attrcount += 1;
     }
 
-    while (true) {
-        if (!dir->erased) {
-            goto compact;
-        }
-
+    if (dir->erased) {
         // try to commit
         struct lfs_commit commit = {
             .block = dir->pair[0],
@@ -1668,18 +1668,15 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
         dir->etag = commit.ptag;
         // successful commit, update globals
         lfs_global_zero(&lfs->locals);
-        break;
-
+    } else {
 compact:
         // fall back to compaction
         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) {
             return err;
         }
-
-        break;
     }
 
     // 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);
 
-        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 == LFS_ERR_CORRUPT) {
                         goto relocate;
                     }
                     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:
         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;
     }
 
+    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) {
         // fill with zeros
         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;
     }
 
-    // update pos
+    // find new pos
+    lfs_off_t npos = file->pos;
     if (whence == LFS_SEEK_SET) {
-        file->pos = off;
+        npos = off;
     } 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) {
-        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) {
@@ -3112,6 +3116,12 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
         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
     lfs->root[0] = 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 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:
@@ -3276,6 +3290,17 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
 
                 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?

+ 17 - 2
lfs.h

@@ -66,6 +66,15 @@ typedef uint32_t lfs_block_t;
 #define LFS_ATTR_MAX 0x1ffe
 #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
 // valid positive return values
 enum lfs_error {
@@ -78,6 +87,7 @@ enum lfs_error {
     LFS_ERR_ISDIR       = -21,  // Entry is a dir
     LFS_ERR_NOTEMPTY    = -39,  // Dir is not empty
     LFS_ERR_BADF        = -9,   // Bad file number
+    LFS_ERR_FBIG        = -27,  // File too large
     LFS_ERR_INVAL       = -22,  // Invalid parameter
     LFS_ERR_NOSPC       = -28,  // No space left on device
     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
     // other littlefs drivers.
     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
@@ -346,6 +361,7 @@ typedef struct lfs_superblock {
     lfs_size_t name_max;
     lfs_size_t inline_max;
     lfs_size_t attr_max;
+    lfs_size_t file_max;
 } lfs_superblock_t;
 
 // The littlefs filesystem type
@@ -378,11 +394,10 @@ typedef struct lfs {
     } free;
 
     const struct lfs_config *cfg;
-    lfs_size_t block_size;
-    lfs_size_t block_count;
     lfs_size_t name_max;
     lfs_size_t inline_max;
     lfs_size_t attr_max;
+    lfs_size_t file_max;
 } lfs_t;
 
 

+ 3 - 3
tests/test_alloc.sh

@@ -32,18 +32,18 @@ lfs_alloc_singleproc() {
 tests/test.py << TEST
     const char *names[] = {"bacon", "eggs", "pancakes"};
     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]);
         lfs_file_open(&lfs, &file[n], (char*)buffer,
                 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]);
         for (int i = 0; i < $SIZE; i++) {
             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_unmount(&lfs) => 0;

+ 61 - 2
tests/test_dirs.sh

@@ -326,13 +326,42 @@ tests/test.py << TEST
     lfs_unmount(&lfs) => 0;
 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 ---"
 tests/test.py << TEST
     lfs_mount(&lfs, &cfg) => 0;
     lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
 
     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;
     }
 
@@ -391,13 +420,43 @@ tests/test.py << TEST
     lfs_unmount(&lfs) => 0;
 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 ---"
 tests/test.py << TEST
     lfs_mount(&lfs, &cfg) => 0;
     lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
 
     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;
     }
 

+ 1 - 1
tests/test_seek.sh

@@ -301,7 +301,7 @@ tests/test.py << TEST
     size = strlen("hedgehoghog");
     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];
         memcpy(buffer, "hedgehoghog", size);
         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;
 
-    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);
         lfs_file_open(&lfs, &file[0], (const char*)buffer,
                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
 
         strcpy((char*)buffer, "hair");
         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_size(&lfs, &file[0]) => startsizes[i];
@@ -55,13 +55,13 @@ tests/test.py << TEST
 
     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);
         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0;
         lfs_file_size(&lfs, &file[0]) => hotsizes[i];
 
         size = strlen("hair");
-        int j = 0;
+        lfs_off_t j = 0;
         for (; j < startsizes[i] && j < hotsizes[i]; j += size) {
             lfs_file_read(&lfs, &file[0], buffer, size) => size;
             memcmp(buffer, "hair", size) => 0;
@@ -87,13 +87,13 @@ tests/test.py << TEST
 
     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);
         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0;
         lfs_file_size(&lfs, &file[0]) => coldsizes[i];
 
         size = strlen("hair");
-        int j = 0;
+        lfs_off_t j = 0;
         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i];
                 j += size) {
             lfs_file_read(&lfs, &file[0], buffer, size) => size;