diff --git a/.travis.yml b/.travis.yml index 255e84b..ccbcaa8 100644 --- a/.travis.yml +++ b/.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 diff --git a/Makefile b/Makefile index a8b8852..79d9156 100644 --- a/Makefile +++ b/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) diff --git a/README.md b/README.md index 45be6f4..d8c2c7b 100644 --- a/README.md +++ b/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. diff --git a/emubd/lfs_emubd.c b/emubd/lfs_emubd.c index de63057..3f31bfa 100644 --- a/emubd/lfs_emubd.c +++ b/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]); } diff --git a/lfs.c b/lfs.c index 32e626f..fc1b7a3 100644 --- a/lfs.c +++ b/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) { + // 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; + } + } + + ackid = id; + } + + // 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; } } - ackid = id; - } + 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; + } + } - // reopen reserved space at the end - commit.end = lfs->cfg->block_size - 8; + 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; + } + } - 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); + err = lfs_commit_crc(lfs, &commit, true); if (err) { if (err == LFS_ERR_CORRUPT) { goto relocate; } 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; - } - 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; } - - 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; - } - } - - 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; 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,60 +2018,8 @@ 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; - } - return err; - } - - if (size == 0) { - *block = nblock; - *off = 0; - return 0; - } - - 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; - } - - err = lfs_bd_prog(lfs, - pcache, rcache, true, - nblock, i, &data, 1); - 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 (true) { + err = lfs_bd_erase(lfs, nblock); if (err) { if (err == LFS_ERR_CORRUPT) { goto relocate; @@ -2082,22 +2027,76 @@ static int lfs_ctz_extend(lfs_t *lfs, 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; - } + if (size == 0) { + *block = nblock; + *off = 0; + return 0; } - LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count); - } + size -= 1; + lfs_off_t index = lfs_ctz_index(lfs, &size); + size += 1; - *block = nblock; - *off = 4*skips; - return 0; + // 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; + } + } + + *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; + } + + 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); + } + + *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; - } - - file->pos = file->ctz.size + off; + npos = file->ctz.size + off; } - return file->pos; + if (npos < 0 || npos > lfs->file_max) { + // file position out of range + return LFS_ERR_INVAL; + } + + // 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, - }; + // 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, + }; - 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; - } + 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; + // 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? diff --git a/lfs.h b/lfs.h index 10cb768..a8071a0 100644 --- a/lfs.h +++ b/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; diff --git a/tests/test_alloc.sh b/tests/test_alloc.sh index 21fee96..f924a29 100755 --- a/tests/test_alloc.sh +++ b/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; diff --git a/tests/test_dirs.sh b/tests/test_dirs.sh index 4aac945..5f2020f 100755 --- a/tests/test_dirs.sh +++ b/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; } diff --git a/tests/test_seek.sh b/tests/test_seek.sh index 609c250..2cd711a 100755 --- a/tests/test_seek.sh +++ b/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; diff --git a/tests/test_truncate.sh b/tests/test_truncate.sh index da5ccaf..053b2e0 100755 --- a/tests/test_truncate.sh +++ b/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;