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

This commit is contained in:
Christopher Haster 2018-10-20 21:02:25 -05:00
commit c8a39c4b23
10 changed files with 370 additions and 241 deletions

View File

@ -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 # Grab latests patch from repo tags, default to 0, needs finagling to get past github's pagination api
- LFS_VERSION_PATCH=$(curl -f -u "$GEKY_BOT_RELEASES" - PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs - PREV_URL=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" -I
| jq 'map(.ref | match( | sed -n '/^Link/{s/.*<\(.*\)>; rel="last"/\1/;p;q0};$q1'
"refs/tags/v'"$LFS_VERSION_MAJOR"'\\.'"$LFS_VERSION_MINOR"'\\.(.*)$") || echo $PREV_URL)
.captures[].string | tonumber + 1) | max // 0') - 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 # Create a simple tag
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
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\", \"ref\": \"refs/tags/$LFS_VERSION\",
\"target_commitish\": \"$TRAVIS_COMMIT\", \"sha\": \"$TRAVIS_COMMIT\"
\"name\": \"${LFS_VERSION%.0}\",
\"body\": $(jq -sR '.' <<< "$CHANGES")
}" }"
# 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

View File

@ -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)

View File

@ -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.

View File

@ -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]);
} }

431
lfs.c
View File

@ -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 if (true) {
uint32_t rev = lfs_tole32(dir->rev); // write out header
err = lfs_commit_prog(lfs, &commit, &rev, sizeof(rev)); uint32_t rev = lfs_tole32(dir->rev);
if (err) { err = lfs_commit_prog(lfs, &commit, &rev, sizeof(rev));
if (err == LFS_ERR_CORRUPT) { if (err) {
goto relocate; if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
} }
return err;
}
// commit with a move // commit with a move
for (uint16_t id = begin; id < end || commit.off < commit.ack; id++) { for (uint16_t id = begin; id < end || commit.off < commit.ack; id++) {
for (int pass = 0; pass < 2; pass++) { for (int pass = 0; pass < 2; pass++) {
err = lfs_commit_move(lfs, &commit, pass, err = lfs_commit_move(lfs, &commit, pass,
0x003fe000, LFS_MKTAG(0, id, 0), 0x003fe000, LFS_MKTAG(0, id, 0),
-LFS_MKTAG(0, begin, 0), -LFS_MKTAG(0, begin, 0),
source, attrs); source, attrs);
if (err && !(splitted && !overcompacting && if (err && !(splitted && !overcompacting &&
err == LFS_ERR_NOSPC)) { err == LFS_ERR_NOSPC)) {
if (!overcompacting && err == LFS_ERR_NOSPC) { if (!overcompacting && err == LFS_ERR_NOSPC) {
goto split; goto split;
} else if (err == LFS_ERR_CORRUPT) { } 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; goto relocate;
} }
return err; 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 if (!lfs_pair_isnull(dir->tail)) {
commit.end = lfs->cfg->block_size - 8; // 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) { err = lfs_commit_crc(lfs, &commit, true);
// 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) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
goto relocate; goto relocate;
} }
return err; return err;
} }
}
if (!relocated && !lfs_global_iszero(&lfs->locals)) { // successful compaction, swap dir pair to indicate most recent
// commit any globals, unless we're relocating, lfs_pair_swap(dir->pair);
// in which case our parent will steal our globals dir->off = commit.off;
err = lfs_commit_globals(lfs, &commit, &lfs->locals); dir->etag = commit.ptag;
if (err) { dir->erased = true;
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
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);
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; 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) {
if (!dir->erased) {
goto compact;
}
// 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,60 +2018,8 @@ 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 (true) {
if (err) { err = lfs_bd_erase(lfs, nblock);
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 (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
goto relocate; goto relocate;
@ -2082,22 +2027,76 @@ static int lfs_ctz_extend(lfs_t *lfs,
return err; return err;
} }
if (i != skips-1) { if (size == 0) {
err = lfs_bd_read(lfs, *block = nblock;
NULL, rcache, sizeof(head), *off = 0;
head, 4*i, &head, sizeof(head)); return 0;
head = lfs_fromle32(head);
if (err) {
return err;
}
} }
LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count); size -= 1;
} lfs_off_t index = lfs_ctz_index(lfs, &size);
size += 1;
*block = nblock; // just copy out the last block if it is incomplete
*off = 4*skips; if (size != lfs->cfg->block_size) {
return 0; 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: 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) { npos = file->pos + off;
return LFS_ERR_INVAL;
}
file->pos = 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) { npos = file->ctz.size + off;
return LFS_ERR_INVAL;
}
file->pos = 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) { 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); int err = 0;
if (err) { if (true) {
return err; err = lfs_init(lfs, cfg);
} if (err) {
return err;
}
// create free lookahead // create free lookahead
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
lfs->free.off = 0; lfs->free.off = 0;
lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size,
lfs->cfg->block_count); lfs->cfg->block_count);
lfs->free.i = 0; lfs->free.i = 0;
lfs_alloc_ack(lfs); lfs_alloc_ack(lfs);
// create root dir // create root dir
lfs_mdir_t root; lfs_mdir_t root;
err = lfs_dir_alloc(lfs, &root); err = lfs_dir_alloc(lfs, &root);
if (err) { if (err) {
goto cleanup; goto cleanup;
} }
// write one superblock // write one superblock
lfs_superblock_t superblock = { lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION, .version = LFS_DISK_VERSION,
.block_size = lfs->cfg->block_size, .block_size = lfs->cfg->block_size,
.block_count = lfs->cfg->block_count, .block_count = lfs->cfg->block_count,
.name_max = lfs->name_max, .name_max = lfs->name_max,
.inline_max = lfs->inline_max, .inline_max = lfs->inline_max,
.attr_max = lfs->attr_max, .attr_max = lfs->attr_max,
}; .file_max = lfs->file_max,
};
lfs_superblock_tole32(&superblock); lfs_superblock_tole32(&superblock);
err = lfs_dir_commit(lfs, &root, err = lfs_dir_commit(lfs, &root,
LFS_MKATTR(LFS_TYPE_INLINESTRUCT, 0, LFS_MKATTR(LFS_TYPE_INLINESTRUCT, 0,
&superblock, sizeof(superblock), &superblock, sizeof(superblock),
LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, "littlefs", 8, LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, "littlefs", 8,
NULL))); NULL)));
if (err) { if (err) {
goto cleanup; goto cleanup;
} }
// sanity check that fetch works // sanity check that fetch works
err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1});
if (err) { if (err) {
goto cleanup; 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?

19
lfs.h
View File

@ -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;

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;