Adopted per-member static configurations

Instead of 1. defining LFS_STATICCFG and 2. defining all LFS_READ_SIZE,
LFS_PROG_SIZE, etc. Configuration can now be made static by defining
LFS_READ_SIZE, LFS_PROG_SIZE, etc. Thanks to a really large ifdef, if
all configurations are provided, LFS_STATICCFG will be defined and the
RAM cost fully removed.

Additionally, we can remove the ROM cost of each struct cfg member,
allowing code savings when config is only partially defined, which is
perhaps more common.

This moves all of the configuration logic in lfs.h, which has the nice
side effect of keeping all of the decision making in the same location.

The only catch is that we need to differentiate between the cfg->*_max
and *_MAX limits. To do this I've renamed the configuration *_max to
*_limit. Note that these two are slightly different, with *_max
indicating the maximum supported by the current driver, and *_limit
the maximum supported by the specific instance of littlefs. However if
you do define a *_LIMIT, I've added an override for the relevant *_MAX,
since I can't think of a time where you _wouldn't_ want to do that.

---

This also required some tweaks in scripts/test.py in order to populate
the lfs_cfg struct correctly. This happens since the test defines
overlap littlefs's configuration defines. This does change what is being
tested a bit, but hopefully that's not a real issue.

Suggested by tim-nordell-nimbelink
This commit is contained in:
Christopher Haster
2020-12-14 23:19:13 -06:00
parent fe42d102a5
commit 5f885f0af1
6 changed files with 427 additions and 326 deletions

View File

@@ -214,14 +214,20 @@ jobs:
- NAME=littlefs-minimal
- CC="arm-linux-gnueabi-gcc --static -mthumb"
- CFLAGS="-Werror
-DLFS_STATICCFG -DLFS_FILE_STATICCFG
-DLFS_BD_READ
-DLFS_BD_PROG
-DLFS_BD_ERASE
-DLFS_BD_SYNC
-DLFS_READ_SIZE=16
-DLFS_PROG_SIZE=16
-DLFS_BLOCK_SIZE=512
-DLFS_BLOCK_COUNT=1024
-DLFS_BLOCK_CYCLES=-1
-DLFS_BLOCK_CYCLES=1024
-DLFS_BUFFER_SIZE=64
-DLFS_LOOKAHEAD_SIZE=16
-DLFS_NAME_LIMIT=0
-DLFS_FILE_LIMIT=0
-DLFS_ATTR_LIMIT=0
-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
if: branch !~ -prefix$
install:

430
lfs.c
View File

@@ -7,68 +7,6 @@
#include "lfs.h"
/// Configuration mapping ///
#ifdef LFS_STATICCFG
// direct config towards user defines
#define LFS_CFG_BD_READ(lfs, block, off, buffer, size) \
((void)lfs, lfs_bd_read(block, off, buffer, size))
#define LFS_CFG_BD_PROG(lfs, block, off, buffer, size) \
((void)lfs, lfs_bd_prog(block, off, buffer, size))
#define LFS_CFG_BD_ERASE(lfs, block) \
((void)lfs, lfs_bd_erase(block))
#define LFS_CFG_BD_SYNC(lfs) \
((void)lfs, lfs_bd_sync())
#define LFS_CFG_READ_SIZE(lfs) ((void)lfs, LFS_READ_SIZE)
#define LFS_CFG_PROG_SIZE(lfs) ((void)lfs, LFS_PROG_SIZE)
#define LFS_CFG_BLOCK_SIZE(lfs) ((void)lfs, LFS_BLOCK_SIZE)
#define LFS_CFG_BLOCK_COUNT(lfs) ((void)lfs, LFS_BLOCK_COUNT)
#define LFS_CFG_BLOCK_CYCLES(lfs) ((void)lfs, LFS_BLOCK_CYCLES)
#define LFS_CFG_BUFFER_SIZE(lfs) ((void)lfs, LFS_BUFFER_SIZE)
#define LFS_CFG_LOOKAHEAD_SIZE(lfs) ((void)lfs, LFS_LOOKAHEAD_SIZE)
#define LFS_CFG_READ_BUFFER(lfs) ((void)lfs, LFS_READ_BUFFER)
#define LFS_CFG_PROG_BUFFER(lfs) ((void)lfs, LFS_PROG_BUFFER)
#define LFS_CFG_LOOKAHEAD_BUFFER(lfs) ((void)lfs, LFS_LOOKAHEAD_BUFFER)
#define LFS_CFG_NAME_MAX(lfs) ((void)lfs, LFS_NAME_MAX)
#define LFS_CFG_FILE_MAX(lfs) ((void)lfs, LFS_FILE_MAX)
#define LFS_CFG_ATTR_MAX(lfs) ((void)lfs, LFS_ATTR_MAX)
#else
// direct config towards dynamic lfs_cfg struct
#define LFS_CFG_BD_READ(lfs, block, off, buffer, size) \
lfs->cfg->bd_read(lfs->cfg->bd_ctx, block, off, buffer, size)
#define LFS_CFG_BD_PROG(lfs, block, off, buffer, size) \
lfs->cfg->bd_prog(lfs->cfg->bd_ctx, block, off, buffer, size)
#define LFS_CFG_BD_ERASE(lfs, block) \
lfs->cfg->bd_erase(lfs->cfg->bd_ctx, block)
#define LFS_CFG_BD_SYNC(lfs) \
lfs->cfg->bd_sync(lfs->cfg->bd_ctx)
#define LFS_CFG_READ_SIZE(lfs) lfs->cfg->read_size
#define LFS_CFG_PROG_SIZE(lfs) lfs->cfg->prog_size
#define LFS_CFG_BLOCK_SIZE(lfs) lfs->cfg->block_size
#define LFS_CFG_BLOCK_COUNT(lfs) lfs->cfg->block_count
#define LFS_CFG_BLOCK_CYCLES(lfs) lfs->cfg->block_cycles
#define LFS_CFG_BUFFER_SIZE(lfs) lfs->cfg->buffer_size
#define LFS_CFG_LOOKAHEAD_SIZE(lfs) lfs->cfg->lookahead_size
#define LFS_CFG_READ_BUFFER(lfs) lfs->cfg->read_buffer
#define LFS_CFG_PROG_BUFFER(lfs) lfs->cfg->prog_buffer
#define LFS_CFG_LOOKAHEAD_BUFFER(lfs) lfs->cfg->lookahead_buffer
#define LFS_CFG_NAME_MAX(lfs) lfs->cfg->name_max
#define LFS_CFG_FILE_MAX(lfs) lfs->cfg->file_max
#define LFS_CFG_ATTR_MAX(lfs) lfs->cfg->attr_max
#endif
#ifdef LFS_FILE_STATICCFG
// direct config towards user defines
#define LFS_FILE_CFG_BUFFER(file) LFS_FILE_BUFFER
#define LFS_FILE_CFG_ATTRS(file) LFS_FILE_ATTRS
#define LFS_FILE_CFG_ATTR_COUNT(file) LFS_FILE_ATTR_COUNT
#else
// direct config towards dynamic lfs_cfg struct
#define LFS_FILE_CFG_BUFFER(file) file->cfg->buffer
#define LFS_FILE_CFG_ATTRS(file) file->cfg->attrs
#define LFS_FILE_CFG_ATTR_COUNT(file) file->cfg->attr_count
#endif
/// Internal littlefs constants ///
#define LFS_BLOCK_NULL ((lfs_block_t)-1)
#define LFS_BLOCK_INLINE ((lfs_block_t)-2)
@@ -83,7 +21,8 @@ static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) {
static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) {
// zero to avoid information leak
memset(pcache->buffer, 0xff, LFS_CFG_BUFFER_SIZE(lfs));
(void)lfs;
memset(pcache->buffer, 0xff, LFS_CFG_BUFFER_SIZE(lfs->cfg));
pcache->block = LFS_BLOCK_NULL;
}
@@ -91,9 +30,10 @@ static int lfs_cache_read(lfs_t *lfs,
const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,
lfs_block_t block, lfs_off_t off,
void *buffer, lfs_size_t size) {
(void)lfs;
uint8_t *data = buffer;
if (block >= LFS_CFG_BLOCK_COUNT(lfs) ||
off+size > LFS_CFG_BLOCK_SIZE(lfs)) {
if (block >= LFS_CFG_BLOCK_COUNT(lfs->cfg) ||
off+size > LFS_CFG_BLOCK_SIZE(lfs->cfg)) {
return LFS_ERR_CORRUPT;
}
@@ -134,11 +74,11 @@ static int lfs_cache_read(lfs_t *lfs,
diff = lfs_min(diff, rcache->off-off);
}
if (size >= hint && off % LFS_CFG_READ_SIZE(lfs) == 0 &&
size >= LFS_CFG_READ_SIZE(lfs)) {
if (size >= hint && off % LFS_CFG_READ_SIZE(lfs->cfg) == 0 &&
size >= LFS_CFG_READ_SIZE(lfs->cfg)) {
// bypass cache?
diff = lfs_aligndown(diff, LFS_CFG_READ_SIZE(lfs));
int err = LFS_CFG_BD_READ(lfs, block, off, data, diff);
diff = lfs_aligndown(diff, LFS_CFG_READ_SIZE(lfs->cfg));
int err = LFS_CFG_BD_READ(lfs->cfg, block, off, data, diff);
if (err) {
return err;
}
@@ -150,16 +90,16 @@ static int lfs_cache_read(lfs_t *lfs,
}
// load to cache, first condition can no longer fail
LFS_ASSERT(block < LFS_CFG_BLOCK_COUNT(lfs));
LFS_ASSERT(block < LFS_CFG_BLOCK_COUNT(lfs->cfg));
rcache->block = block;
rcache->off = lfs_aligndown(off, LFS_CFG_READ_SIZE(lfs));
rcache->off = lfs_aligndown(off, LFS_CFG_READ_SIZE(lfs->cfg));
rcache->size = lfs_min(
lfs_min(
lfs_alignup(off+hint, LFS_CFG_READ_SIZE(lfs)),
LFS_CFG_BLOCK_SIZE(lfs))
lfs_alignup(off+hint, LFS_CFG_READ_SIZE(lfs->cfg)),
LFS_CFG_BLOCK_SIZE(lfs->cfg))
- rcache->off,
LFS_CFG_BUFFER_SIZE(lfs));
int err = LFS_CFG_BD_READ(lfs, rcache->block,
LFS_CFG_BUFFER_SIZE(lfs->cfg));
int err = LFS_CFG_BD_READ(lfs->cfg, rcache->block,
rcache->off, rcache->buffer, rcache->size);
LFS_ASSERT(err <= 0);
if (err) {
@@ -202,9 +142,10 @@ static int lfs_cache_cmp(lfs_t *lfs,
static int lfs_cache_flush(lfs_t *lfs,
lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) {
if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) {
LFS_ASSERT(pcache->block < LFS_CFG_BLOCK_COUNT(lfs));
lfs_size_t diff = lfs_alignup(pcache->size, LFS_CFG_PROG_SIZE(lfs));
int err = LFS_CFG_BD_PROG(lfs, pcache->block,
LFS_ASSERT(pcache->block < LFS_CFG_BLOCK_COUNT(lfs->cfg));
lfs_size_t diff = lfs_alignup(pcache->size,
LFS_CFG_PROG_SIZE(lfs->cfg));
int err = LFS_CFG_BD_PROG(lfs->cfg, pcache->block,
pcache->off, pcache->buffer, diff);
LFS_ASSERT(err <= 0);
if (err) {
@@ -241,7 +182,7 @@ static int lfs_cache_sync(lfs_t *lfs,
return err;
}
err = LFS_CFG_BD_SYNC(lfs);
err = LFS_CFG_BD_SYNC(lfs->cfg);
LFS_ASSERT(err <= 0);
return err;
}
@@ -251,16 +192,17 @@ static int lfs_cache_prog(lfs_t *lfs,
lfs_block_t block, lfs_off_t off,
const void *buffer, lfs_size_t size) {
const uint8_t *data = buffer;
LFS_ASSERT(block == LFS_BLOCK_INLINE || block < LFS_CFG_BLOCK_COUNT(lfs));
LFS_ASSERT(off + size <= LFS_CFG_BLOCK_SIZE(lfs));
LFS_ASSERT(block == LFS_BLOCK_INLINE ||
block < LFS_CFG_BLOCK_COUNT(lfs->cfg));
LFS_ASSERT(off + size <= LFS_CFG_BLOCK_SIZE(lfs->cfg));
while (size > 0) {
if (block == pcache->block &&
off >= pcache->off &&
off < pcache->off + LFS_CFG_BUFFER_SIZE(lfs)) {
off < pcache->off + LFS_CFG_BUFFER_SIZE(lfs->cfg)) {
// already fits in pcache?
lfs_size_t diff = lfs_min(size,
LFS_CFG_BUFFER_SIZE(lfs) - (off-pcache->off));
LFS_CFG_BUFFER_SIZE(lfs->cfg) - (off-pcache->off));
memcpy(&pcache->buffer[off-pcache->off], data, diff);
data += diff;
@@ -268,7 +210,7 @@ static int lfs_cache_prog(lfs_t *lfs,
size -= diff;
pcache->size = lfs_max(pcache->size, off - pcache->off);
if (pcache->size == LFS_CFG_BUFFER_SIZE(lfs)) {
if (pcache->size == LFS_CFG_BUFFER_SIZE(lfs->cfg)) {
// eagerly flush out pcache if we fill up
int err = lfs_cache_flush(lfs, pcache, rcache, validate);
if (err) {
@@ -285,7 +227,7 @@ static int lfs_cache_prog(lfs_t *lfs,
// prepare pcache, first condition can no longer fail
pcache->block = block;
pcache->off = lfs_aligndown(off, LFS_CFG_PROG_SIZE(lfs));
pcache->off = lfs_aligndown(off, LFS_CFG_PROG_SIZE(lfs->cfg));
pcache->size = 0;
}
@@ -293,8 +235,9 @@ static int lfs_cache_prog(lfs_t *lfs,
}
static int lfs_cache_erase(lfs_t *lfs, lfs_block_t block) {
LFS_ASSERT(block < LFS_CFG_BLOCK_COUNT(lfs));
int err = LFS_CFG_BD_ERASE(lfs, block);
(void)lfs;
LFS_ASSERT(block < LFS_CFG_BLOCK_COUNT(lfs->cfg));
int err = LFS_CFG_BD_ERASE(lfs->cfg, block);
LFS_ASSERT(err <= 0);
return err;
}
@@ -460,18 +403,18 @@ static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) {
superblock->version = lfs_fromle32(superblock->version);
superblock->block_size = lfs_fromle32(superblock->block_size);
superblock->block_count = lfs_fromle32(superblock->block_count);
superblock->name_max = lfs_fromle32(superblock->name_max);
superblock->file_max = lfs_fromle32(superblock->file_max);
superblock->attr_max = lfs_fromle32(superblock->attr_max);
superblock->name_limit = lfs_fromle32(superblock->name_limit);
superblock->file_limit = lfs_fromle32(superblock->file_limit);
superblock->attr_limit = lfs_fromle32(superblock->attr_limit);
}
static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
superblock->version = lfs_tole32(superblock->version);
superblock->block_size = lfs_tole32(superblock->block_size);
superblock->block_count = lfs_tole32(superblock->block_count);
superblock->name_max = lfs_tole32(superblock->name_max);
superblock->file_max = lfs_tole32(superblock->file_max);
superblock->attr_max = lfs_tole32(superblock->attr_max);
superblock->name_limit = lfs_tole32(superblock->name_limit);
superblock->file_limit = lfs_tole32(superblock->file_limit);
superblock->attr_limit = lfs_tole32(superblock->attr_limit);
}
@@ -506,7 +449,7 @@ static int lfs1_traverse(lfs_t *lfs,
static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
lfs_t *lfs = (lfs_t*)p;
lfs_block_t off = ((block - lfs->free.off)
+ LFS_CFG_BLOCK_COUNT(lfs)) % LFS_CFG_BLOCK_COUNT(lfs);
+ LFS_CFG_BLOCK_COUNT(lfs->cfg)) % LFS_CFG_BLOCK_COUNT(lfs->cfg);
if (off < lfs->free.size) {
lfs->free.buffer[off / 32] |= 1U << (off % 32);
@@ -516,13 +459,13 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
}
static void lfs_alloc_ack(lfs_t *lfs) {
lfs->free.ack = LFS_CFG_BLOCK_COUNT(lfs);
lfs->free.ack = LFS_CFG_BLOCK_COUNT(lfs->cfg);
}
// Invalidate the lookahead buffer. This is done during mounting and
// failed traversals
static void lfs_alloc_reset(lfs_t *lfs) {
lfs->free.off = lfs->seed % LFS_CFG_BLOCK_SIZE(lfs);
lfs->free.off = lfs->seed % LFS_CFG_BLOCK_SIZE(lfs->cfg);
lfs->free.size = 0;
lfs->free.i = 0;
lfs_alloc_ack(lfs);
@@ -537,7 +480,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) {
// found a free block
*block = (lfs->free.off + off) % LFS_CFG_BLOCK_COUNT(lfs);
*block = (lfs->free.off + off) % LFS_CFG_BLOCK_COUNT(lfs->cfg);
// eagerly find next off so an alloc ack can
// discredit old lookahead blocks
@@ -560,12 +503,14 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
}
lfs->free.off = (lfs->free.off + lfs->free.size)
% LFS_CFG_BLOCK_COUNT(lfs);
lfs->free.size = lfs_min(8*LFS_CFG_LOOKAHEAD_SIZE(lfs), lfs->free.ack);
% LFS_CFG_BLOCK_COUNT(lfs->cfg);
lfs->free.size = lfs_min(
8*LFS_CFG_LOOKAHEAD_SIZE(lfs->cfg),
lfs->free.ack);
lfs->free.i = 0;
// find mask of free blocks from tree
memset(lfs->free.buffer, 0, LFS_CFG_LOOKAHEAD_SIZE(lfs));
memset(lfs->free.buffer, 0, LFS_CFG_LOOKAHEAD_SIZE(lfs->cfg));
int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true);
if (err) {
lfs_alloc_reset(lfs);
@@ -649,7 +594,7 @@ static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir,
lfs_tag_t gmask, lfs_tag_t gtag,
lfs_off_t off, void *buffer, lfs_size_t size) {
uint8_t *data = buffer;
if (off+size > LFS_CFG_BLOCK_SIZE(lfs)) {
if (off+size > LFS_CFG_BLOCK_SIZE(lfs->cfg)) {
return LFS_ERR_CORRUPT;
}
@@ -692,9 +637,10 @@ static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir,
// load to cache, first condition can no longer fail
rcache->block = LFS_BLOCK_INLINE;
rcache->off = lfs_aligndown(off, LFS_CFG_READ_SIZE(lfs));
rcache->size = lfs_min(lfs_alignup(off+hint, LFS_CFG_READ_SIZE(lfs)),
LFS_CFG_BUFFER_SIZE(lfs));
rcache->off = lfs_aligndown(off, LFS_CFG_READ_SIZE(lfs->cfg));
rcache->size = lfs_min(
lfs_alignup(off+hint, LFS_CFG_READ_SIZE(lfs->cfg)),
LFS_CFG_BUFFER_SIZE(lfs->cfg));
int err = lfs_dir_getslice(lfs, dir, gmask, gtag,
rcache->off, rcache->buffer, rcache->size);
if (err < 0) {
@@ -837,8 +783,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// if either block address is invalid we return LFS_ERR_CORRUPT here,
// otherwise later writes to the pair could fail
if (pair[0] >= LFS_CFG_BLOCK_COUNT(lfs) ||
pair[1] >= LFS_CFG_BLOCK_COUNT(lfs)) {
if (pair[0] >= LFS_CFG_BLOCK_COUNT(lfs->cfg) ||
pair[1] >= LFS_CFG_BLOCK_COUNT(lfs->cfg)) {
return LFS_ERR_CORRUPT;
}
@@ -884,7 +830,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
lfs_tag_t tag;
off += lfs_tag_dsize(ptag);
int err = lfs_cache_read(lfs,
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs),
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs->cfg),
dir->pair[0], off, &tag, sizeof(tag));
if (err) {
if (err == LFS_ERR_CORRUPT) {
@@ -901,9 +847,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// next commit not yet programmed or we're not in valid range
if (!lfs_tag_isvalid(tag)) {
dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC &&
dir->off % LFS_CFG_PROG_SIZE(lfs) == 0);
dir->off % LFS_CFG_PROG_SIZE(lfs->cfg) == 0);
break;
} else if (off + lfs_tag_dsize(tag) > LFS_CFG_BLOCK_SIZE(lfs)) {
} else if (off + lfs_tag_dsize(tag)
> LFS_CFG_BLOCK_SIZE(lfs->cfg)) {
dir->erased = false;
break;
}
@@ -914,7 +861,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// check the crc attr
uint32_t dcrc;
err = lfs_cache_read(lfs,
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs),
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs->cfg),
dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc));
if (err) {
if (err == LFS_ERR_CORRUPT) {
@@ -955,7 +902,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
for (lfs_off_t j = sizeof(tag); j < lfs_tag_dsize(tag); j++) {
uint8_t dat;
err = lfs_cache_read(lfs,
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs),
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs->cfg),
dir->pair[0], off+j, &dat, 1);
if (err) {
if (err == LFS_ERR_CORRUPT) {
@@ -988,7 +935,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
tempsplit = (lfs_tag_chunk(tag) & 1);
err = lfs_cache_read(lfs,
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs),
NULL, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs->cfg),
dir->pair[0], off+sizeof(tag), &temptail, 8);
if (err) {
if (err == LFS_ERR_CORRUPT) {
@@ -1099,7 +1046,7 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
}
lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name);
LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_limit+1), info->name);
if (tag < 0) {
return (int)tag;
}
@@ -1329,7 +1276,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
const uint32_t crc1 = commit->crc;
// align to program units
const lfs_off_t end = lfs_alignup(off1 + 2*sizeof(uint32_t),
LFS_CFG_PROG_SIZE(lfs));
LFS_CFG_PROG_SIZE(lfs->cfg));
// create crc tags to fill up remainder of commit, note that
// padding is not crced, which lets fetches skip padding but
@@ -1551,9 +1498,9 @@ static int lfs_dir_compact(lfs_t *lfs,
// cleanup delete, and we cap at half a block to give room
// for metadata updates.
if (end - begin < 0xff &&
size <= lfs_min(LFS_CFG_BLOCK_SIZE(lfs) - 36,
lfs_alignup(LFS_CFG_BLOCK_SIZE(lfs)/2,
LFS_CFG_PROG_SIZE(lfs)))) {
size <= lfs_min(LFS_CFG_BLOCK_SIZE(lfs->cfg) - 36,
lfs_alignup(LFS_CFG_BLOCK_SIZE(lfs->cfg)/2,
LFS_CFG_PROG_SIZE(lfs->cfg)))) {
break;
}
@@ -1567,7 +1514,8 @@ static int lfs_dir_compact(lfs_t *lfs,
// if we fail to split, we may be able to overcompact, unless
// we're too big for even the full block, in which case our
// only option is to error
if (err == LFS_ERR_NOSPC && size <= LFS_CFG_BLOCK_SIZE(lfs) - 36) {
if (err == LFS_ERR_NOSPC && size
<= LFS_CFG_BLOCK_SIZE(lfs->cfg) - 36) {
break;
}
return err;
@@ -1584,8 +1532,8 @@ static int lfs_dir_compact(lfs_t *lfs,
// 1. block_cycles = 1, which would prevent relocations from terminating
// 2. block_cycles = 2n, which, due to aliasing, would only ever relocate
// one metadata block in the pair, effectively making this useless
if (LFS_CFG_BLOCK_CYCLES(lfs) > 0 &&
(dir->rev % ((LFS_CFG_BLOCK_CYCLES(lfs)+1)|1) == 0)) {
if (LFS_CFG_BLOCK_CYCLES(lfs->cfg) > 0 &&
(dir->rev % ((LFS_CFG_BLOCK_CYCLES(lfs->cfg)+1)|1) == 0)) {
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
// oh no! we're writing too much to the superblock,
// should we expand?
@@ -1596,7 +1544,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// do we have extra space? littlefs can't reclaim this space
// by itself, so expand cautiously
if ((lfs_size_t)res < LFS_CFG_BLOCK_COUNT(lfs)/2) {
if ((lfs_size_t)res < LFS_CFG_BLOCK_COUNT(lfs->cfg)/2) {
LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev);
int err = lfs_dir_split(lfs, dir, attrs, attrcount,
source, begin, end);
@@ -1637,7 +1585,7 @@ static int lfs_dir_compact(lfs_t *lfs,
.crc = 0xffffffff,
.begin = 0,
.end = LFS_CFG_BLOCK_SIZE(lfs) - 8,
.end = LFS_CFG_BLOCK_SIZE(lfs->cfg) - 8,
};
// erase block to write to
@@ -1728,7 +1676,7 @@ static int lfs_dir_compact(lfs_t *lfs,
}
// successful compaction, swap dir pair to indicate most recent
LFS_ASSERT(commit.off % LFS_CFG_PROG_SIZE(lfs) == 0);
LFS_ASSERT(commit.off % LFS_CFG_PROG_SIZE(lfs->cfg) == 0);
lfs_pair_swap(dir->pair);
dir->count = end - begin;
dir->off = commit.off;
@@ -1787,7 +1735,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 &&
f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) &&
f->ctz.size > LFS_CFG_BUFFER_SIZE(lfs)) {
f->ctz.size > LFS_CFG_BUFFER_SIZE(lfs->cfg)) {
int err = lfs_file_outline(lfs, f);
if (err) {
return err;
@@ -1845,7 +1793,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
.crc = 0xffffffff,
.begin = dir->off,
.end = LFS_CFG_BLOCK_SIZE(lfs) - 8,
.end = LFS_CFG_BLOCK_SIZE(lfs->cfg) - 8,
};
// traverse attrs that need to be written out
@@ -1901,7 +1849,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
}
// successful commit, update dir
LFS_ASSERT(commit.off % LFS_CFG_PROG_SIZE(lfs) == 0);
LFS_ASSERT(commit.off % LFS_CFG_PROG_SIZE(lfs->cfg) == 0);
dir->off = commit.off;
dir->etag = commit.ptag;
// and update gstate
@@ -1990,7 +1938,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
// check that name fits
lfs_size_t nlen = strlen(path);
if (nlen > lfs->name_max) {
if (nlen > lfs->name_limit) {
LFS_TRACE("lfs_mkdir -> %d", LFS_ERR_NAMETOOLONG);
return LFS_ERR_NAMETOOLONG;
}
@@ -2255,8 +2203,9 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
/// File index list operations ///
static int lfs_ctz_index(lfs_t *lfs, lfs_off_t *off) {
(void)lfs;
lfs_off_t size = *off;
lfs_off_t b = LFS_CFG_BLOCK_SIZE(lfs) - 2*4;
lfs_off_t b = LFS_CFG_BLOCK_SIZE(lfs->cfg) - 2*4;
lfs_off_t i = size / b;
if (i == 0) {
return 0;
@@ -2333,7 +2282,7 @@ static int lfs_ctz_extend(lfs_t *lfs,
noff = noff + 1;
// just copy out the last block if it is incomplete
if (noff != LFS_CFG_BLOCK_SIZE(lfs)) {
if (noff != LFS_CFG_BLOCK_SIZE(lfs->cfg)) {
for (lfs_off_t i = 0; i < noff; i++) {
uint8_t data;
err = lfs_cache_read(lfs,
@@ -2481,7 +2430,7 @@ static int lfs_file_opencommon(lfs_t *lfs, lfs_file_t *file,
// check that name fits
lfs_size_t nlen = strlen(path);
if (nlen > lfs->name_max) {
if (nlen > lfs->name_limit) {
err = LFS_ERR_NAMETOOLONG;
goto cleanup;
}
@@ -2518,16 +2467,17 @@ static int lfs_file_opencommon(lfs_t *lfs, lfs_file_t *file,
lfs_ctz_fromle32(&file->ctz);
}
#if !defined(LFS_FILE_STATICCFG) || LFS_FILE_ATTR_COUNT > 0
#if !defined(LFS_FILE_ATTR_COUNT) || LFS_FILE_ATTR_COUNT > 0
// fetch attrs
for (unsigned i = 0; i < LFS_FILE_CFG_ATTR_COUNT(file); i++) {
for (unsigned i = 0; i < LFS_FILE_CFG_ATTR_COUNT(file->cfg); i++) {
if ((file->flags & 3) != LFS_O_WRONLY) {
lfs_stag_t res = lfs_dir_get(lfs, &file->m,
LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(
LFS_TYPE_USERATTR + LFS_FILE_CFG_ATTRS(file)[i].type,
file->id, LFS_FILE_CFG_ATTRS(file)[i].size),
LFS_FILE_CFG_ATTRS(file)[i].buffer);
LFS_TYPE_USERATTR
+ LFS_FILE_CFG_ATTRS(file->cfg)[i].type,
file->id, LFS_FILE_CFG_ATTRS(file->cfg)[i].size),
LFS_FILE_CFG_ATTRS(file->cfg)[i].buffer);
if (res < 0 && res != LFS_ERR_NOENT) {
err = res;
goto cleanup;
@@ -2535,7 +2485,7 @@ static int lfs_file_opencommon(lfs_t *lfs, lfs_file_t *file,
}
if ((file->flags & 3) != LFS_O_RDONLY) {
if (LFS_FILE_CFG_ATTRS(file)[i].size > lfs->attr_max) {
if (LFS_FILE_CFG_ATTRS(file->cfg)[i].size > lfs->attr_limit) {
err = LFS_ERR_NOSPC;
goto cleanup;
}
@@ -2546,10 +2496,10 @@ static int lfs_file_opencommon(lfs_t *lfs, lfs_file_t *file,
#endif
// allocate buffer if needed
if (LFS_FILE_CFG_BUFFER(file)) {
file->cache.buffer = LFS_FILE_CFG_BUFFER(file);
if (LFS_FILE_CFG_BUFFER(file->cfg)) {
file->cache.buffer = LFS_FILE_CFG_BUFFER(file->cfg);
} else {
file->cache.buffer = lfs_malloc(LFS_CFG_BUFFER_SIZE(lfs));
file->cache.buffer = lfs_malloc(LFS_CFG_BUFFER_SIZE(lfs->cfg));
if (!file->cache.buffer) {
err = LFS_ERR_NOMEM;
goto cleanup;
@@ -2566,7 +2516,7 @@ static int lfs_file_opencommon(lfs_t *lfs, lfs_file_t *file,
file->flags |= LFS_F_INLINE;
file->cache.block = file->ctz.head;
file->cache.off = 0;
file->cache.size = LFS_CFG_BUFFER_SIZE(lfs);
file->cache.size = LFS_CFG_BUFFER_SIZE(lfs->cfg);
// don't always read (may be new/trunc file)
if (file->ctz.size > 0) {
@@ -2634,7 +2584,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
}
// clean up memory
if (!LFS_FILE_CFG_BUFFER(file)) {
if (!LFS_FILE_CFG_BUFFER(file->cfg)) {
lfs_free(file->cache.buffer);
}
@@ -2696,7 +2646,8 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
}
// copy over new state of file
memcpy(file->cache.buffer, lfs->pcache.buffer, LFS_CFG_BUFFER_SIZE(lfs));
memcpy(file->cache.buffer, lfs->pcache.buffer,
LFS_CFG_BUFFER_SIZE(lfs->cfg));
file->cache.block = lfs->pcache.block;
file->cache.off = lfs->pcache.off;
file->cache.size = lfs->pcache.size;
@@ -2773,7 +2724,8 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
// write out what we have
while (true) {
int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache, true);
int err = lfs_cache_flush(lfs,
&file->cache, &lfs->rcache, true);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
@@ -2849,7 +2801,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS(
{LFS_MKTAG(type, file->id, size), buffer},
{LFS_MKTAG(LFS_FROM_USERATTRS, file->id,
LFS_FILE_CFG_ATTR_COUNT(file)), LFS_FILE_CFG_ATTRS(file)}));
LFS_FILE_CFG_ATTR_COUNT(file->cfg)),
LFS_FILE_CFG_ATTRS(file->cfg)}));
if (err) {
file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_sync -> %d", err);
@@ -2894,7 +2847,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
while (nsize > 0) {
// check if we need a new block
if (!(file->flags & LFS_F_READING) ||
file->off == LFS_CFG_BLOCK_SIZE(lfs)) {
file->off == LFS_CFG_BLOCK_SIZE(lfs->cfg)) {
if (!(file->flags & LFS_F_INLINE)) {
int err = lfs_ctz_find(lfs, NULL, &file->cache,
file->ctz.head, file->ctz.size,
@@ -2912,10 +2865,11 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
}
// read as much as we can in current block
lfs_size_t diff = lfs_min(nsize, LFS_CFG_BLOCK_SIZE(lfs) - file->off);
lfs_size_t diff = lfs_min(nsize,
LFS_CFG_BLOCK_SIZE(lfs->cfg) - file->off);
if (file->flags & LFS_F_INLINE) {
int err = lfs_dir_getread(lfs, &file->m,
NULL, &file->cache, LFS_CFG_BLOCK_SIZE(lfs),
NULL, &file->cache, LFS_CFG_BLOCK_SIZE(lfs->cfg),
LFS_MKTAG(0xfff, 0x1ff, 0),
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
file->off, data, diff);
@@ -2925,7 +2879,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
}
} else {
int err = lfs_cache_read(lfs,
NULL, &file->cache, LFS_CFG_BLOCK_SIZE(lfs),
NULL, &file->cache, LFS_CFG_BLOCK_SIZE(lfs->cfg),
file->block, file->off, data, diff);
if (err) {
LFS_TRACE("lfs_file_read -> %d", err);
@@ -2966,7 +2920,7 @@ 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) {
if (file->pos + size > lfs->file_limit) {
// Larger than file limit?
LFS_TRACE("lfs_file_write -> %d", LFS_ERR_FBIG);
return LFS_ERR_FBIG;
@@ -2989,7 +2943,8 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
if ((file->flags & LFS_F_INLINE) &&
lfs_max(file->pos+nsize, file->ctz.size) >
lfs_min(0x3fe, lfs_min(
LFS_CFG_BUFFER_SIZE(lfs), LFS_CFG_BLOCK_SIZE(lfs)/8))) {
LFS_CFG_BUFFER_SIZE(lfs->cfg),
LFS_CFG_BLOCK_SIZE(lfs->cfg)/8))) {
// inline file doesn't fit anymore
int err = lfs_file_outline(lfs, file);
if (err) {
@@ -3002,7 +2957,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
while (nsize > 0) {
// check if we need a new block
if (!(file->flags & LFS_F_WRITING) ||
file->off == LFS_CFG_BLOCK_SIZE(lfs)) {
file->off == LFS_CFG_BLOCK_SIZE(lfs->cfg)) {
if (!(file->flags & LFS_F_INLINE)) {
if (!(file->flags & LFS_F_WRITING) && file->pos > 0) {
// find out which block we're extending from
@@ -3038,7 +2993,8 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
}
// program as much as we can in current block
lfs_size_t diff = lfs_min(nsize, LFS_CFG_BLOCK_SIZE(lfs) - file->off);
lfs_size_t diff = lfs_min(nsize,
LFS_CFG_BLOCK_SIZE(lfs->cfg) - file->off);
while (true) {
int err = lfs_cache_prog(lfs, &file->cache, &lfs->rcache, true,
file->block, file->off, data, diff);
@@ -3097,7 +3053,7 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
npos = file->ctz.size + off;
}
if (npos > lfs->file_max) {
if (npos > lfs->file_limit) {
// file position out of range
LFS_TRACE("lfs_file_seek -> %d", LFS_ERR_INVAL);
return LFS_ERR_INVAL;
@@ -3343,7 +3299,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
if (prevtag == LFS_ERR_NOENT) {
// check that name fits
lfs_size_t nlen = strlen(newpath);
if (nlen > lfs->name_max) {
if (nlen > lfs->name_limit) {
LFS_TRACE("lfs_rename -> %d", LFS_ERR_NAMETOOLONG);
return LFS_ERR_NAMETOOLONG;
}
@@ -3473,7 +3429,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
tag = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_USERATTR + type,
id, lfs_min(size, lfs->attr_max)),
id, lfs_min(size, lfs->attr_limit)),
buffer);
if (tag < 0) {
if (tag == LFS_ERR_NOENT) {
@@ -3516,7 +3472,7 @@ int lfs_setattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size) {
LFS_TRACE("lfs_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")",
(void*)lfs, path, type, buffer, size);
if (size > lfs->attr_max) {
if (size > lfs->attr_limit) {
LFS_TRACE("lfs_setattr -> %d", LFS_ERR_NOSPC);
return LFS_ERR_NOSPC;
}
@@ -3540,19 +3496,22 @@ static int lfs_initcommon(lfs_t *lfs) {
// validate that the lfs-cfg sizes were initiated properly before
// performing any arithmetic logics with them
LFS_ASSERT(LFS_CFG_READ_SIZE(lfs) != 0);
LFS_ASSERT(LFS_CFG_PROG_SIZE(lfs) != 0);
LFS_ASSERT(LFS_CFG_BUFFER_SIZE(lfs) != 0);
LFS_ASSERT(LFS_CFG_READ_SIZE(lfs->cfg) != 0);
LFS_ASSERT(LFS_CFG_PROG_SIZE(lfs->cfg) != 0);
LFS_ASSERT(LFS_CFG_BUFFER_SIZE(lfs->cfg) != 0);
// check that block size is a multiple of cache size is a multiple
// of prog and read sizes
LFS_ASSERT(LFS_CFG_BUFFER_SIZE(lfs) % LFS_CFG_READ_SIZE(lfs) == 0);
LFS_ASSERT(LFS_CFG_BUFFER_SIZE(lfs) % LFS_CFG_PROG_SIZE(lfs) == 0);
LFS_ASSERT(LFS_CFG_BLOCK_SIZE(lfs) % LFS_CFG_BUFFER_SIZE(lfs) == 0);
LFS_ASSERT(LFS_CFG_BUFFER_SIZE(lfs->cfg)
% LFS_CFG_READ_SIZE(lfs->cfg) == 0);
LFS_ASSERT(LFS_CFG_BUFFER_SIZE(lfs->cfg)
% LFS_CFG_PROG_SIZE(lfs->cfg) == 0);
LFS_ASSERT(LFS_CFG_BLOCK_SIZE(lfs->cfg)
% LFS_CFG_BUFFER_SIZE(lfs->cfg) == 0);
// check that the block size is large enough to fit ctz pointers
LFS_ASSERT(4*lfs_npw2(0xffffffff / (LFS_CFG_BLOCK_SIZE(lfs)-2*4))
<= LFS_CFG_BLOCK_SIZE(lfs));
LFS_ASSERT(4*lfs_npw2(0xffffffff / (LFS_CFG_BLOCK_SIZE(lfs->cfg)-2*4))
<= LFS_CFG_BLOCK_SIZE(lfs->cfg));
// block_cycles = 0 is no longer supported.
//
@@ -3560,14 +3519,13 @@ static int lfs_initcommon(lfs_t *lfs) {
// metadata logs as a part of wear leveling. Suggested values are in the
// range of 100-1000, or set block_cycles to -1 to disable block-level
// wear-leveling.
LFS_ASSERT(LFS_CFG_BLOCK_CYCLES(lfs) != 0);
LFS_ASSERT(LFS_CFG_BLOCK_CYCLES(lfs->cfg) != 0);
// setup read cache
if (LFS_CFG_READ_BUFFER(lfs)) {
lfs->rcache.buffer = LFS_CFG_READ_BUFFER(lfs);
if (LFS_CFG_READ_BUFFER(lfs->cfg)) {
lfs->rcache.buffer = LFS_CFG_READ_BUFFER(lfs->cfg);
} else {
lfs->rcache.buffer = lfs_malloc(LFS_CFG_BUFFER_SIZE(lfs));
lfs->rcache.buffer = lfs_malloc(LFS_CFG_BUFFER_SIZE(lfs->cfg));
if (!lfs->rcache.buffer) {
err = LFS_ERR_NOMEM;
goto cleanup;
@@ -3575,10 +3533,10 @@ static int lfs_initcommon(lfs_t *lfs) {
}
// setup program cache
if (LFS_CFG_PROG_BUFFER(lfs)) {
lfs->pcache.buffer = LFS_CFG_PROG_BUFFER(lfs);
if (LFS_CFG_PROG_BUFFER(lfs->cfg)) {
lfs->pcache.buffer = LFS_CFG_PROG_BUFFER(lfs->cfg);
} else {
lfs->pcache.buffer = lfs_malloc(LFS_CFG_BUFFER_SIZE(lfs));
lfs->pcache.buffer = lfs_malloc(LFS_CFG_BUFFER_SIZE(lfs->cfg));
if (!lfs->pcache.buffer) {
err = LFS_ERR_NOMEM;
goto cleanup;
@@ -3590,13 +3548,13 @@ static int lfs_initcommon(lfs_t *lfs) {
lfs_cache_zero(lfs, &lfs->pcache);
// setup lookahead, must be multiple of 64-bits, 32-bit aligned
LFS_ASSERT(LFS_CFG_LOOKAHEAD_SIZE(lfs) > 0);
LFS_ASSERT(LFS_CFG_LOOKAHEAD_SIZE(lfs) % 8 == 0 &&
(uintptr_t)LFS_CFG_LOOKAHEAD_BUFFER(lfs) % 4 == 0);
if (LFS_CFG_LOOKAHEAD_BUFFER(lfs)) {
lfs->free.buffer = LFS_CFG_LOOKAHEAD_BUFFER(lfs);
LFS_ASSERT(LFS_CFG_LOOKAHEAD_SIZE(lfs->cfg) > 0);
LFS_ASSERT(LFS_CFG_LOOKAHEAD_SIZE(lfs->cfg) % 8 == 0 &&
(uintptr_t)LFS_CFG_LOOKAHEAD_BUFFER(lfs->cfg) % 4 == 0);
if (LFS_CFG_LOOKAHEAD_BUFFER(lfs->cfg)) {
lfs->free.buffer = LFS_CFG_LOOKAHEAD_BUFFER(lfs->cfg);
} else {
lfs->free.buffer = lfs_malloc(LFS_CFG_LOOKAHEAD_SIZE(lfs));
lfs->free.buffer = lfs_malloc(LFS_CFG_LOOKAHEAD_SIZE(lfs->cfg));
if (!lfs->free.buffer) {
err = LFS_ERR_NOMEM;
goto cleanup;
@@ -3604,22 +3562,22 @@ static int lfs_initcommon(lfs_t *lfs) {
}
// check that the size limits are sane
LFS_ASSERT(LFS_CFG_NAME_MAX(lfs) <= LFS_NAME_MAX);
lfs->name_max = LFS_CFG_NAME_MAX(lfs);
if (!lfs->name_max) {
lfs->name_max = LFS_NAME_MAX;
LFS_ASSERT(LFS_CFG_NAME_LIMIT(lfs->cfg) <= LFS_NAME_MAX);
lfs->name_limit = LFS_CFG_NAME_LIMIT(lfs->cfg);
if (!lfs->name_limit) {
lfs->name_limit = LFS_NAME_MAX;
}
LFS_ASSERT(LFS_CFG_FILE_MAX(lfs) <= LFS_FILE_MAX);
lfs->file_max = LFS_CFG_FILE_MAX(lfs);
if (!lfs->file_max) {
lfs->file_max = LFS_FILE_MAX;
LFS_ASSERT(LFS_CFG_FILE_LIMIT(lfs->cfg) <= LFS_FILE_MAX);
lfs->file_limit = LFS_CFG_FILE_LIMIT(lfs->cfg);
if (!lfs->file_limit) {
lfs->file_limit = LFS_FILE_MAX;
}
LFS_ASSERT(LFS_CFG_ATTR_MAX(lfs) <= LFS_ATTR_MAX);
lfs->attr_max = LFS_CFG_ATTR_MAX(lfs);
if (!lfs->attr_max) {
lfs->attr_max = LFS_ATTR_MAX;
LFS_ASSERT(LFS_CFG_ATTR_LIMIT(lfs->cfg) <= LFS_ATTR_MAX);
lfs->attr_limit = LFS_CFG_ATTR_LIMIT(lfs->cfg);
if (!lfs->attr_limit) {
lfs->attr_limit = LFS_ATTR_MAX;
}
// setup default state
@@ -3643,15 +3601,15 @@ cleanup:
static int lfs_deinit(lfs_t *lfs) {
// free allocated memory
if (!LFS_CFG_READ_BUFFER(lfs)) {
if (!LFS_CFG_READ_BUFFER(lfs->cfg)) {
lfs_free(lfs->rcache.buffer);
}
if (!LFS_CFG_PROG_BUFFER(lfs)) {
if (!LFS_CFG_PROG_BUFFER(lfs->cfg)) {
lfs_free(lfs->pcache.buffer);
}
if (!LFS_CFG_LOOKAHEAD_BUFFER(lfs)) {
if (!LFS_CFG_LOOKAHEAD_BUFFER(lfs->cfg)) {
lfs_free(lfs->free.buffer);
}
@@ -3667,10 +3625,10 @@ static int lfs_formatcommon(lfs_t *lfs) {
}
// create free lookahead
memset(lfs->free.buffer, 0, LFS_CFG_LOOKAHEAD_SIZE(lfs));
memset(lfs->free.buffer, 0, LFS_CFG_LOOKAHEAD_SIZE(lfs->cfg));
lfs->free.off = 0;
lfs->free.size = lfs_min(8*LFS_CFG_LOOKAHEAD_SIZE(lfs),
LFS_CFG_BLOCK_COUNT(lfs));
lfs->free.size = lfs_min(8*LFS_CFG_LOOKAHEAD_SIZE(lfs->cfg),
LFS_CFG_BLOCK_COUNT(lfs->cfg));
lfs->free.i = 0;
lfs_alloc_ack(lfs);
@@ -3684,11 +3642,11 @@ static int lfs_formatcommon(lfs_t *lfs) {
// write one superblock
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION,
.block_size = LFS_CFG_BLOCK_SIZE(lfs),
.block_count = LFS_CFG_BLOCK_COUNT(lfs),
.name_max = lfs->name_max,
.file_max = lfs->file_max,
.attr_max = lfs->attr_max,
.block_size = LFS_CFG_BLOCK_SIZE(lfs->cfg),
.block_count = LFS_CFG_BLOCK_COUNT(lfs->cfg),
.name_limit = lfs->name_limit,
.file_limit = lfs->file_limit,
.attr_limit = lfs->attr_limit,
};
lfs_superblock_tole32(&superblock);
@@ -3739,15 +3697,15 @@ int lfs_formatcfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
".lookahead_size=%"PRIu32", .read_buffer=%p, "
".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", "
".attr_max=%"PRIu32"})",
".name_limit=%"PRIu32", .file_limit=%"PRIu32", "
".attr_limit=%"PRIu32"})",
(void*)lfs, (void*)cfg, cfg->ctx,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
cfg->block_cycles, cfg->cache_size, cfg->lookahead_size,
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
cfg->name_limit, cfg->file_limit, cfg->attr_limit);
lfs->cfg = cfg;
int err = lfs_formatcommon(lfs);
LFS_TRACE("lfs_formatcfg -> %d", err);
@@ -3765,7 +3723,7 @@ static int lfs_mountcommon(lfs_t *lfs) {
lfs_mdir_t dir = {.tail = {0, 1}};
lfs_block_t cycle = 0;
while (!lfs_pair_isnull(dir.tail)) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs)/2) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs->cfg)/2) {
// loop detected
err = LFS_ERR_CORRUPT;
goto cleanup;
@@ -3813,37 +3771,37 @@ static int lfs_mountcommon(lfs_t *lfs) {
}
// check superblock configuration
if (superblock.name_max) {
if (superblock.name_max > lfs->name_max) {
LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")",
superblock.name_max, lfs->name_max);
if (superblock.name_limit) {
if (superblock.name_limit > lfs->name_limit) {
LFS_ERROR("Unsupported name_limit (%"PRIu32" > %"PRIu32")",
superblock.name_limit, lfs->name_limit);
err = LFS_ERR_INVAL;
goto cleanup;
}
lfs->name_max = superblock.name_max;
lfs->name_limit = superblock.name_limit;
}
if (superblock.file_max) {
if (superblock.file_max > lfs->file_max) {
LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")",
superblock.file_max, lfs->file_max);
if (superblock.file_limit) {
if (superblock.file_limit > lfs->file_limit) {
LFS_ERROR("Unsupported file_limit (%"PRIu32" > %"PRIu32")",
superblock.file_limit, lfs->file_limit);
err = LFS_ERR_INVAL;
goto cleanup;
}
lfs->file_max = superblock.file_max;
lfs->file_limit = superblock.file_limit;
}
if (superblock.attr_max) {
if (superblock.attr_max > lfs->attr_max) {
LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")",
superblock.attr_max, lfs->attr_max);
if (superblock.attr_limit) {
if (superblock.attr_limit > lfs->attr_limit) {
LFS_ERROR("Unsupported attr_limit (%"PRIu32" > %"PRIu32")",
superblock.attr_limit, lfs->attr_limit);
err = LFS_ERR_INVAL;
goto cleanup;
}
lfs->attr_max = superblock.attr_max;
lfs->attr_limit = superblock.attr_limit;
}
}
@@ -3900,15 +3858,15 @@ int lfs_mountcfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
".lookahead_size=%"PRIu32", .read_buffer=%p, "
".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", "
".attr_max=%"PRIu32"})",
".name_limit=%"PRIu32", .file_limit=%"PRIu32", "
".attr_limit=%"PRIu32"})",
(void*)lfs, (void*)cfg, cfg->ctx,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
cfg->block_cycles, cfg->cache_size, cfg->lookahead_size,
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
cfg->name_limit, cfg->file_limit, cfg->attr_limit);
lfs->cfg = cfg;
int err = lfs_mountcommon(lfs);
LFS_TRACE("lfs_mountcfg -> %d", err);
@@ -3946,7 +3904,7 @@ int lfs_fs_traverseraw(lfs_t *lfs,
lfs_block_t cycle = 0;
while (!lfs_pair_isnull(dir.tail)) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs)/2) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs->cfg)/2) {
// loop detected
return LFS_ERR_CORRUPT;
}
@@ -4037,7 +3995,7 @@ static int lfs_fs_pred(lfs_t *lfs,
pdir->tail[1] = 1;
lfs_block_t cycle = 0;
while (!lfs_pair_isnull(pdir->tail)) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs)/2) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs->cfg)/2) {
// loop detected
return LFS_ERR_CORRUPT;
}
@@ -4070,7 +4028,7 @@ static int lfs_fs_parent_match(void *data,
lfs_block_t child[2];
int err = lfs_cache_read(lfs,
&lfs->pcache, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs),
&lfs->pcache, &lfs->rcache, LFS_CFG_BLOCK_SIZE(lfs->cfg),
disk->block, disk->off, &child, sizeof(child));
if (err) {
return err;
@@ -4087,7 +4045,7 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
parent->tail[1] = 1;
lfs_block_t cycle = 0;
while (!lfs_pair_isnull(parent->tail)) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs)/2) {
if (cycle >= LFS_CFG_BLOCK_COUNT(lfs->cfg)/2) {
// loop detected
return LFS_ERR_CORRUPT;
}
@@ -4529,7 +4487,7 @@ static int lfs1_dir_fetch(lfs_t *lfs,
}
if ((0x7fffffff & test.size) < sizeof(test)+4 ||
(0x7fffffff & test.size) > LFS_CFG_BLOCK_SIZE(lfs)) {
(0x7fffffff & test.size) > LFS_CFG_BLOCK_SIZE(lfs->cfg)) {
continue;
}
@@ -4962,11 +4920,11 @@ static int lfs_migratecommon(lfs_t *lfs) {
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION,
.block_size = LFS_CFG_BLOCK_SIZE(lfs),
.block_count = LFS_CFG_BLOCK_COUNT(lfs),
.name_max = lfs->name_max,
.file_max = lfs->file_max,
.attr_max = lfs->attr_max,
.block_size = LFS_CFG_BLOCK_SIZE(lfs->cfg),
.block_count = LFS_CFG_BLOCK_COUNT(lfs->cfg),
.name_limit = lfs->name_limit,
.file_limit = lfs->file_limit,
.attr_limit = lfs->attr_limit,
};
lfs_superblock_tole32(&superblock);
@@ -5016,15 +4974,15 @@ int lfs_migratecfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
".lookahead_size=%"PRIu32", .read_buffer=%p, "
".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", "
".attr_max=%"PRIu32"})",
".name_limit=%"PRIu32", .file_limit=%"PRIu32", "
".attr_limit=%"PRIu32"})",
(void*)lfs, (void*)cfg, cfg->ctx,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
cfg->block_cycles, cfg->cache_size, cfg->lookahead_size,
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
cfg->name_limit, cfg->file_limit, cfg->attr_limit);
lfs->cfg = cfg;
int err = lfs_migratecommon(lfs);
LFS_TRACE("lfs_migratecfg -> %d", err);

252
lfs.h
View File

@@ -46,8 +46,13 @@ typedef uint32_t lfs_block_t;
// info struct. Limited to <= 1022. Stored in superblock and must be
// respected by other littlefs drivers.
#ifndef LFS_NAME_MAX
#if defined(LFS_NAME_LIMIT) && \
LFS_NAME_LIMIT > 0 && LFS_NAME_MAX <= 1022
#define LFS_NAME_MAX LFS_NAME_LIMIT
#else
#define LFS_NAME_MAX 255
#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
@@ -55,14 +60,24 @@ typedef uint32_t lfs_block_t;
// incorrect values due to using signed integers. Stored in superblock and
// must be respected by other littlefs drivers.
#ifndef LFS_FILE_MAX
#if defined(LFS_FILE_LIMIT) && \
LFS_FILE_LIMIT > 0 && LFS_FILE_LIMIT <= 4294967296
#define LFS_FILE_MAX LFS_FILE_LIMIT
#else
#define LFS_FILE_MAX 2147483647
#endif
#endif
// Maximum size of custom attributes in bytes, may be redefined, but there is
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022.
#ifndef LFS_ATTR_MAX
#if defined(LFS_ATTR_LIMIT) && \
LFS_ATTR_LIMIT > 0 && LFS_ATTR_LIMIT <= 1022
#define LFS_ATTR_MAX LFS_FILE_LIMIT
#else
#define LFS_ATTR_MAX 1022
#endif
#endif
// File types
enum lfs_type {
@@ -125,50 +140,129 @@ enum lfs_whence_flags {
};
#if !defined(LFS_STATICCFG)
// Configuration provided during initialization of the littlefs
// If every config option is provided at compile time, littlefs switches
// to "LFS_STATICCFG" mode. The dynamic lfs_cfg struct is not included in
// the lfs_t struct, and *cfg functions are no longer available.
#if defined(LFS_BD_READ) && \
defined(LFS_BD_PROG) && \
defined(LFS_BD_ERASE) && \
defined(LFS_BD_SYNC) && \
defined(LFS_READ_SIZE) && \
defined(LFS_PROG_SIZE) && \
defined(LFS_BLOCK_SIZE) && \
defined(LFS_BLOCK_COUNT) && \
defined(LFS_BLOCK_CYCLES) && \
defined(LFS_BUFFER_SIZE) && \
defined(LFS_LOOKAHEAD_SIZE) && \
defined(LFS_READ_BUFFER) && \
defined(LFS_PROG_BUFFER) && \
defined(LFS_LOOKAHEAD_BUFFER) && \
defined(LFS_NAME_LIMIT) && \
defined(LFS_FILE_LIMIT) && \
defined(LFS_ATTR_LIMIT)
#define LFS_STATICCFG
#endif
// Dynamic config struct
#ifndef LFS_STATICCFG
struct lfs_cfg {
#endif
// Opaque user provided context that can be used to pass
// information to the block device operations
#if !(defined(LFS_BD_READ) && \
defined(LFS_BD_PROG) && \
defined(LFS_BD_ERASE) && \
defined(LFS_BD_SYNC))
void *bd_ctx;
#endif
// Read a region in a block. Negative error codes are propogated
// to the user.
#ifndef LFS_BD_READ
int (*bd_read)(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
#define LFS_CFG_BD_READ(cfg, block, off, buffer, size) \
(cfg)->bd_read((cfg)->bd_ctx, block, off, buffer, size)
#else
#define LFS_CFG_BD_READ(cfg, block, off, buffer, size) \
lfs_bd_read(block, off, buffer, size)
#endif
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
#ifndef LFS_BD_PROG
int (*bd_prog)(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
#define LFS_CFG_BD_PROG(cfg, block, off, buffer, size) \
(cfg)->bd_prog((cfg)->bd_ctx, block, off, buffer, size)
#else
#define LFS_CFG_BD_PROG(cfg, block, off, buffer, size) \
lfs_bd_prog(block, off, buffer, size)
#endif
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
#ifndef LFS_BD_ERASE
int (*bd_erase)(void *ctx, lfs_block_t block);
#define LFS_CFG_BD_ERASE(cfg, block) \
(cfg)->bd_erase((cfg)->bd_ctx, block)
#else
#define LFS_CFG_BD_ERASE(cfg, block) \
lfs_bd_erase(block)
#endif
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.
#ifndef LFS_BD_SYNC
int (*bd_sync)(void *ctx);
#define LFS_CFG_BD_SYNC(cfg) \
(cfg)->bd_sync((cfg)->bd_ctx)
#else
#define LFS_CFG_BD_SYNC(cfg) \
lfs_bd_sync()
#endif
// Minimum size of a block read. All read operations will be a
// multiple of this value.
#ifndef LFS_READ_SIZE
lfs_size_t read_size;
#define LFS_CFG_READ_SIZE(cfg) (cfg)->read_size
#else
#define LFS_CFG_READ_SIZE(cfg) LFS_READ_SIZE
#endif
// Minimum size of a block program. All program operations will be a
// multiple of this value.
#ifndef LFS_PROG_SIZE
lfs_size_t prog_size;
#define LFS_CFG_PROG_SIZE(cfg) (cfg)->prog_size
#else
#define LFS_CFG_PROG_SIZE(cfg) LFS_PROG_SIZE
#endif
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, non-inlined files
// take up at minimum one block. Must be a multiple of the read
// and program sizes.
#ifndef LFS_BLOCK_SIZE
lfs_size_t block_size;
#define LFS_CFG_BLOCK_SIZE(cfg) (cfg)->block_size
#else
#define LFS_CFG_BLOCK_SIZE(cfg) LFS_BLOCK_SIZE
#endif
// Number of erasable blocks on the device.
#ifndef LFS_BLOCK_COUNT
lfs_size_t block_count;
#define LFS_CFG_BLOCK_COUNT(cfg) (cfg)->block_count
#else
#define LFS_CFG_BLOCK_COUNT(cfg) LFS_BLOCK_COUNT
#endif
// Number of erase cycles before littlefs evicts metadata logs and moves
// the metadata to another block. Suggested values are in the
@@ -176,104 +270,139 @@ struct lfs_cfg {
// of less consistent wear distribution.
//
// Set to -1 to disable block-level wear-leveling.
#ifndef LFS_BLOCK_CYCLES
int32_t block_cycles;
#define LFS_CFG_BLOCK_CYCLES(cfg) (cfg)->block_cycles
#else
#define LFS_CFG_BLOCK_CYCLES(cfg) LFS_BLOCK_CYCLES
#endif
// Size of internal buffers used to cache slices of blocks in RAM.
// The littlefs needs a read buffer, a program buffer, and one additional
// buffer per file. Larger buffers can improve performance by storing more
// data and reducing the number of disk accesses. Must be a multiple of
// the read and program sizes, and a factor of the block size.
#ifndef LFS_BUFFER_SIZE
lfs_size_t buffer_size;
#define LFS_CFG_BUFFER_SIZE(cfg) (cfg)->buffer_size
#else
#define LFS_CFG_BUFFER_SIZE(cfg) LFS_BUFFER_SIZE
#endif
// Size of the lookahead buffer in bytes. A larger lookahead buffer
// increases the number of blocks found during an allocation pass. The
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
// can track 8 blocks. Must be a multiple of 8.
#ifndef LFS_LOOKAHEAD_SIZE
lfs_size_t lookahead_size;
#define LFS_CFG_LOOKAHEAD_SIZE(cfg) (cfg)->lookahead_size
#else
#define LFS_CFG_LOOKAHEAD_SIZE(cfg) LFS_LOOKAHEAD_SIZE
#endif
// Optional statically allocated read buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
#ifndef LFS_READ_BUFFER
void *read_buffer;
#define LFS_CFG_READ_BUFFER(cfg) (cfg)->read_buffer
#else
#define LFS_CFG_READ_BUFFER(cfg) LFS_READ_BUFFER
#endif
// Optional statically allocated program buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
#ifndef LFS_PROG_BUFFER
void *prog_buffer;
#define LFS_CFG_PROG_BUFFER(cfg) (cfg)->prog_buffer
#else
#define LFS_CFG_PROG_BUFFER(cfg) LFS_PROG_BUFFER
#endif
// Optional statically allocated lookahead buffer. Must be lookahead_size
// and aligned to a 32-bit boundary. By default lfs_malloc is used to
// allocate this buffer.
#ifndef LFS_LOOKAHEAD_BUFFER
void *lookahead_buffer;
#define LFS_CFG_LOOKAHEAD_BUFFER(cfg) (cfg)->lookahead_buffer
#else
#define LFS_CFG_LOOKAHEAD_BUFFER(cfg) LFS_LOOKAHEAD_BUFFER
#endif
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers.
lfs_size_t name_max;
#ifndef LFS_NAME_LIMIT
lfs_size_t name_limit;
#define LFS_CFG_NAME_LIMIT(cfg) (cfg)->name_limit
#else
#define LFS_CFG_NAME_LIMIT(cfg) LFS_NAME_LIMIT
#endif
// 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;
#ifndef LFS_FILE_LIMIT
lfs_size_t file_limit;
#define LFS_CFG_FILE_LIMIT(cfg) (cfg)->file_limit
#else
#define LFS_CFG_FILE_LIMIT(cfg) LFS_FILE_LIMIT
#endif
// Optional upper limit on custom attributes in bytes. No downside for
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
// LFS_ATTR_MAX when zero.
lfs_size_t attr_max;
#ifndef LFS_ATTR_LIMIT
lfs_size_t attr_limit;
#define LFS_CFG_ATTR_LIMIT(cfg) (cfg)->attr_limit
#else
#define LFS_CFG_ATTR_LIMIT(cfg) LFS_ATTR_LIMIT
#endif
#ifndef LFS_STATICCFG
};
#else
// Static configuration if LFS_STATICCFG is defined, there are defaults
// for some of these, but some are required. For full documentation, see
// the lfs_cfg struct above.
#endif
// Block device operations
int lfs_bd_read(lfs_block_t block,
// Configurable callbacks are a bit special, when LFS_BD_* is defined,
// LFS_CFG_* instead expands into a call to an extern lfs_bd_*, which
// must be defined by the user. This preserves type-safety of the
// callbacks.
#ifdef LFS_BD_READ
extern int lfs_bd_read(lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
int lfs_bd_prog(lfs_block_t block,
#endif
#ifdef LFS_BD_PROG
extern int lfs_bd_prog(lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
int lfs_bd_erase(lfs_block_t block);
int lfs_bd_sync(void);
// Required configuration
#ifndef LFS_READ_SIZE
#error "LFS_STATICCFG requires LFS_READ_SIZE"
#endif
#ifndef LFS_PROG_SIZE
#error "LFS_STATICCFG requires LFS_PROG_SIZE"
#ifdef LFS_BD_ERASE
extern int lfs_bd_erase(lfs_block_t block);
#endif
#ifndef LFS_BLOCK_SIZE
#error "LFS_STATICCFG requires LFS_BLOCK_SIZE"
#endif
#ifndef LFS_BLOCK_COUNT
#error "LFS_STATICCFG requires LFS_BLOCK_COUNT"
#endif
#ifndef LFS_BLOCK_CYCLES
#error "LFS_STATICCFG requires LFS_BLOCK_CYCLES"
#endif
#ifndef LFS_BUFFER_SIZE
#error "LFS_STATICCFG requires LFS_BUFFER_SIZE"
#endif
#ifndef LFS_LOOKAHEAD_SIZE
#error "LFS_STATICCFG requires LFS_LOOKAHEAD_SIZE"
#ifdef LFS_BD_SYNC
extern int lfs_bd_sync(void);
#endif
// Optional configuration
#ifndef LFS_READ_BUFFER
#define LFS_READ_BUFFER NULL
#endif
#ifndef LFS_PROG_BUFFER
#define LFS_PROG_BUFFER NULL
#endif
#ifndef LFS_LOOKAHEAD_BUFFER
#define LFS_LOOKAHEAD_BUFFER NULL
#endif
// If every config option is provided at compile time, littlefs switches
// to "LFS_FILE_STATICCFG" mode. The dynamic lfs_file_cfg struct is not
// included in the lfs_file_t struct, and *cfg functions are no longer
// available.
#if defined(LFS_FILE_BUFFER) && \
defined(LFS_FILE_ATTRS) && \
defined(LFS_FILE_ATTR_COUNT)
#define LFS_STATICCFG
#endif
#if !defined(LFS_FILE_STATICCFG)
#ifndef LFS_FILE_STATICCFG
// Optional configuration provided during lfs_file_opencfg
struct lfs_file_cfg {
#endif
// Optional statically allocated file buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
#ifndef LFS_FILE_BUFFER
void *buffer;
#define LFS_FILE_CFG_BUFFER(cfg) (cfg)->buffer
#else
#define LFS_FILE_CFG_BUFFER(cfg) LFS_FILE_BUFFER
#endif
// Optional list of custom attributes related to the file. If the file
// is opened with read access, these attributes will be read from disk
@@ -286,23 +415,22 @@ struct lfs_file_cfg {
// than the buffer, it will be padded with zeros. If the stored attribute
// is larger, then it will be silently truncated. If the attribute is not
// found, it will be created implicitly.
#ifndef LFS_FILE_ATTRS
struct lfs_attr *attrs;
#define LFS_FILE_CFG_ATTRS(cfg) (cfg)->attrs
#else
#define LFS_FILE_CFG_ATTRS(cfg) LFS_FILE_ATTRS
#endif
// Number of custom attributes in the list
#ifndef LFS_FILE_ATTR_COUNT
lfs_size_t attr_count;
#define LFS_FILE_CFG_ATTR_COUNT(cfg) (cfg)->attr_count
#else
#define LFS_FILE_CFG_ATTR_COUNT(cfg) LFS_FILE_ATTR_COUNT
#endif
#ifndef LFS_FILE_STATICCFG
};
#else
// Static configuration if LFS_FILE_STATICCFG is defined. For full
// documentation, see the lfs_file_cfg struct above.
#ifndef LFS_FILE_BUFFER
#define LFS_FILE_BUFFER NULL
#endif
#ifndef LFS_FILE_ATTRS
#define LFS_FILE_ATTRS ((struct lfs_attr*)NULL)
#endif
#ifndef LFS_FILE_ATTR_COUNT
#define LFS_FILE_ATTR_COUNT 0
#endif
#endif
// File info structure
@@ -392,9 +520,9 @@ typedef struct lfs_superblock {
uint32_t version;
lfs_size_t block_size;
lfs_size_t block_count;
lfs_size_t name_max;
lfs_size_t file_max;
lfs_size_t attr_max;
lfs_size_t name_limit;
lfs_size_t file_limit;
lfs_size_t attr_limit;
} lfs_superblock_t;
typedef struct lfs_gstate {
@@ -431,9 +559,9 @@ typedef struct lfs {
#ifndef LFS_STATICCFG
const struct lfs_cfg *cfg;
#endif
lfs_size_t name_max;
lfs_size_t file_max;
lfs_size_t attr_max;
lfs_size_t name_limit;
lfs_size_t file_limit;
lfs_size_t attr_limit;
#ifdef LFS_MIGRATE
struct lfs1 *lfs1;

View File

@@ -84,6 +84,17 @@ DEFINES = {
'LFS_ERASE_CYCLES': 0,
'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_PROGERROR',
}
CFG = {
'read_size': 'LFS_READ_SIZE',
'prog_size': 'LFS_PROG_SIZE',
'read_size': 'LFS_READ_SIZE',
'prog_size': 'LFS_PROG_SIZE',
'block_size': 'LFS_BLOCK_SIZE',
'block_count': 'LFS_BLOCK_COUNT',
'block_cycles': 'LFS_BLOCK_CYCLES',
'buffer_size': 'LFS_BUFFER_SIZE',
'lookahead_size': 'LFS_LOOKAHEAD_SIZE',
}
PROLOGUE = """
// prologue
__attribute__((unused)) lfs_t lfs;
@@ -97,18 +108,12 @@ PROLOGUE = """
__attribute__((unused)) int err;
__attribute__((unused)) const struct lfs_cfg cfg = {
.bd_ctx = &bd,
.bd_read = lfs_testbd_readctx,
.bd_prog = lfs_testbd_progctx,
.bd_erase = lfs_testbd_erasectx,
.bd_sync = lfs_testbd_syncctx,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.block_cycles = LFS_BLOCK_CYCLES,
.buffer_size = LFS_BUFFER_SIZE,
.lookahead_size = LFS_LOOKAHEAD_SIZE,
.bd_ctx = &bd,
.bd_read = lfs_testbd_readctx,
.bd_prog = lfs_testbd_progctx,
.bd_erase = lfs_testbd_erasectx,
.bd_sync = lfs_testbd_syncctx,
%(cfg)s
};
__attribute__((unused)) const struct lfs_testbd_cfg bdcfg = {
@@ -202,7 +207,11 @@ class TestCase:
for k in sorted(self.perms[0].defines)
if k not in self.defines)))
f.write(PROLOGUE)
f.write(PROLOGUE % dict(
cfg='\n'.join(
8*' '+'.%s = %s,\n' % (k, d)
for k, d in sorted(CFG.items())
if d not in self.suite.defines)))
f.write('\n')
f.write(4*' '+'// test case %d\n' % self.caseno)
f.write(4*' '+'#line %d "%s"\n' % (self.code_lineno, self.suite.path))

View File

@@ -492,17 +492,17 @@ code = '''
// create one block hole for half a directory
lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
for (lfs_size_t i = 0; i < LFS_BLOCK_SIZE; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file, buffer, cfg.block_size) => cfg.block_size;
lfs_file_write(&lfs, &file, buffer, LFS_BLOCK_SIZE) => LFS_BLOCK_SIZE;
lfs_file_close(&lfs, &file) => 0;
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-4)*(cfg.block_size-8);
i < (LFS_BLOCK_COUNT-4)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
@@ -518,10 +518,10 @@ code = '''
lfs_mkdir(&lfs, "splitdir") => 0;
lfs_file_open(&lfs, &file, "splitdir/bump",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
for (lfs_size_t i = 0; i < LFS_BLOCK_SIZE; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file, buffer, 2*cfg.block_size) => LFS_ERR_NOSPC;
lfs_file_write(&lfs, &file, buffer, 2*LFS_BLOCK_SIZE) => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
@@ -541,7 +541,7 @@ code = '''
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i < ((LFS_BLOCK_COUNT-2)/2)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
@@ -552,7 +552,7 @@ code = '''
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i < ((LFS_BLOCK_COUNT-2+1)/2)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
@@ -569,7 +569,7 @@ code = '''
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i < ((LFS_BLOCK_COUNT-2)/2)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
@@ -583,7 +583,7 @@ code = '''
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i < ((LFS_BLOCK_COUNT-2+1)/2)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
@@ -606,7 +606,7 @@ code = '''
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i < ((LFS_BLOCK_COUNT-2)/2)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
@@ -617,7 +617,7 @@ code = '''
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i < ((LFS_BLOCK_COUNT-2+1)/2)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
@@ -634,7 +634,7 @@ code = '''
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8);
i < ((LFS_BLOCK_COUNT-2)/2 - 1)*(LFS_BLOCK_SIZE-8);
i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}

View File

@@ -100,7 +100,7 @@ code = '''
lfs_file_open(&lfs, &file, "sequence",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
size = lfs_min(lfs.cfg->buffer_size, sizeof(buffer)/2);
size = lfs_min(LFS_BUFFER_SIZE, sizeof(buffer)/2);
lfs_size_t qsize = size / 4;
uint8_t *wb = buffer;
uint8_t *rb = buffer + size;