summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDaniël van de Giessen <daniel@dvdgiessen.nl>2024-03-27 20:16:13 +0100
committerDamien George <damien@micropython.org>2025-05-07 17:01:16 +1000
commit9287a1e6eaa9c6dc1bb9be27466acd3e639d8113 (patch)
tree97c7613a503fbe0e8d735d6b28fb59c3a6fa8c44
parent80d03b77804da44d56b7b9446520ffafde7e3eea (diff)
downloadmicropython-9287a1e6eaa9c6dc1bb9be27466acd3e639d8113.tar.gz
micropython-9287a1e6eaa9c6dc1bb9be27466acd3e639d8113.zip
lib/littlefs: Update LittleFS to v2.10.2.
Going above the root directory (/../foo) now gives an error. This is an intentional change made by LittleFS. It required a update of the testsuite and is a (minor) compatibility break. Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
-rw-r--r--lib/littlefs/lfs2.c785
-rw-r--r--lib/littlefs/lfs2.h94
-rw-r--r--lib/littlefs/lfs2_util.c3
-rw-r--r--lib/littlefs/lfs2_util.h40
-rw-r--r--tests/extmod/vfs_lfs.py2
5 files changed, 585 insertions, 339 deletions
diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c
index d89c42fd59..f9ce2cf290 100644
--- a/lib/littlefs/lfs2.c
+++ b/lib/littlefs/lfs2.c
@@ -282,6 +282,21 @@ static int lfs2_bd_erase(lfs2_t *lfs2, lfs2_block_t block) {
/// Small type-level utilities ///
+
+// some operations on paths
+static inline lfs2_size_t lfs2_path_namelen(const char *path) {
+ return strcspn(path, "/");
+}
+
+static inline bool lfs2_path_islast(const char *path) {
+ lfs2_size_t namelen = lfs2_path_namelen(path);
+ return path[namelen + strspn(path + namelen, "/")] == '\0';
+}
+
+static inline bool lfs2_path_isdir(const char *path) {
+ return path[lfs2_path_namelen(path)] != '\0';
+}
+
// operations on block pairs
static inline void lfs2_pair_swap(lfs2_block_t pair[2]) {
lfs2_block_t t = pair[0];
@@ -389,18 +404,15 @@ struct lfs2_diskoff {
// operations on global state
static inline void lfs2_gstate_xor(lfs2_gstate_t *a, const lfs2_gstate_t *b) {
- for (int i = 0; i < 3; i++) {
- ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i];
- }
+ a->tag ^= b->tag;
+ a->pair[0] ^= b->pair[0];
+ a->pair[1] ^= b->pair[1];
}
static inline bool lfs2_gstate_iszero(const lfs2_gstate_t *a) {
- for (int i = 0; i < 3; i++) {
- if (((uint32_t*)a)[i] != 0) {
- return false;
- }
- }
- return true;
+ return a->tag == 0
+ && a->pair[0] == 0
+ && a->pair[1] == 0;
}
#ifndef LFS2_READONLY
@@ -550,9 +562,9 @@ static int lfs2_dir_compact(lfs2_t *lfs2,
lfs2_mdir_t *source, uint16_t begin, uint16_t end);
static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file,
const void *buffer, lfs2_size_t size);
-static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file,
+static lfs2_ssize_t lfs2_file_write_(lfs2_t *lfs2, lfs2_file_t *file,
const void *buffer, lfs2_size_t size);
-static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file);
+static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file);
static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file);
static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file);
@@ -574,65 +586,72 @@ static int lfs21_traverse(lfs2_t *lfs2,
int (*cb)(void*, lfs2_block_t), void *data);
#endif
-static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir);
+static int lfs2_dir_rewind_(lfs2_t *lfs2, lfs2_dir_t *dir);
static lfs2_ssize_t lfs2_file_flushedread(lfs2_t *lfs2, lfs2_file_t *file,
void *buffer, lfs2_size_t size);
-static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file,
+static lfs2_ssize_t lfs2_file_read_(lfs2_t *lfs2, lfs2_file_t *file,
void *buffer, lfs2_size_t size);
-static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file);
-static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file);
+static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file);
+static lfs2_soff_t lfs2_file_size_(lfs2_t *lfs2, lfs2_file_t *file);
-static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2);
-static int lfs2_fs_rawtraverse(lfs2_t *lfs2,
+static lfs2_ssize_t lfs2_fs_size_(lfs2_t *lfs2);
+static int lfs2_fs_traverse_(lfs2_t *lfs2,
int (*cb)(void *data, lfs2_block_t block), void *data,
bool includeorphans);
static int lfs2_deinit(lfs2_t *lfs2);
-static int lfs2_rawunmount(lfs2_t *lfs2);
+static int lfs2_unmount_(lfs2_t *lfs2);
/// Block allocator ///
+
+// allocations should call this when all allocated blocks are committed to
+// the filesystem
+//
+// after a checkpoint, the block allocator may realloc any untracked blocks
+static void lfs2_alloc_ckpoint(lfs2_t *lfs2) {
+ lfs2->lookahead.ckpoint = lfs2->block_count;
+}
+
+// drop the lookahead buffer, this is done during mounting and failed
+// traversals in order to avoid invalid lookahead state
+static void lfs2_alloc_drop(lfs2_t *lfs2) {
+ lfs2->lookahead.size = 0;
+ lfs2->lookahead.next = 0;
+ lfs2_alloc_ckpoint(lfs2);
+}
+
#ifndef LFS2_READONLY
static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) {
lfs2_t *lfs2 = (lfs2_t*)p;
- lfs2_block_t off = ((block - lfs2->free.off)
+ lfs2_block_t off = ((block - lfs2->lookahead.start)
+ lfs2->block_count) % lfs2->block_count;
- if (off < lfs2->free.size) {
- lfs2->free.buffer[off / 32] |= 1U << (off % 32);
+ if (off < lfs2->lookahead.size) {
+ lfs2->lookahead.buffer[off / 8] |= 1U << (off % 8);
}
return 0;
}
#endif
-// indicate allocated blocks have been committed into the filesystem, this
-// is to prevent blocks from being garbage collected in the middle of a
-// commit operation
-static void lfs2_alloc_ack(lfs2_t *lfs2) {
- lfs2->free.ack = lfs2->block_count;
-}
-
-// drop the lookahead buffer, this is done during mounting and failed
-// traversals in order to avoid invalid lookahead state
-static void lfs2_alloc_drop(lfs2_t *lfs2) {
- lfs2->free.size = 0;
- lfs2->free.i = 0;
- lfs2_alloc_ack(lfs2);
-}
-
#ifndef LFS2_READONLY
-static int lfs2_fs_rawgc(lfs2_t *lfs2) {
- // Move free offset at the first unused block (lfs2->free.i)
- // lfs2->free.i is equal lfs2->free.size when all blocks are used
- lfs2->free.off = (lfs2->free.off + lfs2->free.i) % lfs2->block_count;
- lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->free.ack);
- lfs2->free.i = 0;
+static int lfs2_alloc_scan(lfs2_t *lfs2) {
+ // move lookahead buffer to the first unused block
+ //
+ // note we limit the lookahead buffer to at most the amount of blocks
+ // checkpointed, this prevents the math in lfs2_alloc from underflowing
+ lfs2->lookahead.start = (lfs2->lookahead.start + lfs2->lookahead.next)
+ % lfs2->block_count;
+ lfs2->lookahead.next = 0;
+ lfs2->lookahead.size = lfs2_min(
+ 8*lfs2->cfg->lookahead_size,
+ lfs2->lookahead.ckpoint);
// find mask of free blocks from tree
- memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size);
- int err = lfs2_fs_rawtraverse(lfs2, lfs2_alloc_lookahead, lfs2, true);
+ memset(lfs2->lookahead.buffer, 0, lfs2->cfg->lookahead_size);
+ int err = lfs2_fs_traverse_(lfs2, lfs2_alloc_lookahead, lfs2, true);
if (err) {
lfs2_alloc_drop(lfs2);
return err;
@@ -645,36 +664,49 @@ static int lfs2_fs_rawgc(lfs2_t *lfs2) {
#ifndef LFS2_READONLY
static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) {
while (true) {
- while (lfs2->free.i != lfs2->free.size) {
- lfs2_block_t off = lfs2->free.i;
- lfs2->free.i += 1;
- lfs2->free.ack -= 1;
-
- if (!(lfs2->free.buffer[off / 32] & (1U << (off % 32)))) {
+ // scan our lookahead buffer for free blocks
+ while (lfs2->lookahead.next < lfs2->lookahead.size) {
+ if (!(lfs2->lookahead.buffer[lfs2->lookahead.next / 8]
+ & (1U << (lfs2->lookahead.next % 8)))) {
// found a free block
- *block = (lfs2->free.off + off) % lfs2->block_count;
-
- // eagerly find next off so an alloc ack can
- // discredit old lookahead blocks
- while (lfs2->free.i != lfs2->free.size &&
- (lfs2->free.buffer[lfs2->free.i / 32]
- & (1U << (lfs2->free.i % 32)))) {
- lfs2->free.i += 1;
- lfs2->free.ack -= 1;
+ *block = (lfs2->lookahead.start + lfs2->lookahead.next)
+ % lfs2->block_count;
+
+ // eagerly find next free block to maximize how many blocks
+ // lfs2_alloc_ckpoint makes available for scanning
+ while (true) {
+ lfs2->lookahead.next += 1;
+ lfs2->lookahead.ckpoint -= 1;
+
+ if (lfs2->lookahead.next >= lfs2->lookahead.size
+ || !(lfs2->lookahead.buffer[lfs2->lookahead.next / 8]
+ & (1U << (lfs2->lookahead.next % 8)))) {
+ return 0;
+ }
}
-
- return 0;
}
+
+ lfs2->lookahead.next += 1;
+ lfs2->lookahead.ckpoint -= 1;
}
- // check if we have looked at all blocks since last ack
- if (lfs2->free.ack == 0) {
- LFS2_ERROR("No more free space %"PRIu32,
- lfs2->free.i + lfs2->free.off);
+ // In order to keep our block allocator from spinning forever when our
+ // filesystem is full, we mark points where there are no in-flight
+ // allocations with a checkpoint before starting a set of allocations.
+ //
+ // If we've looked at all blocks since the last checkpoint, we report
+ // the filesystem as out of storage.
+ //
+ if (lfs2->lookahead.ckpoint <= 0) {
+ LFS2_ERROR("No more free space 0x%"PRIx32,
+ (lfs2->lookahead.start + lfs2->lookahead.next)
+ % lfs2->block_count);
return LFS2_ERR_NOSPC;
}
- int err = lfs2_fs_rawgc(lfs2);
+ // No blocks in our lookahead buffer, we need to scan the filesystem for
+ // unused blocks in the next lookahead window.
+ int err = lfs2_alloc_scan(lfs2);
if(err) {
return err;
}
@@ -690,11 +722,14 @@ static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir,
lfs2_tag_t ntag = dir->etag;
lfs2_stag_t gdiff = 0;
+ // synthetic moves
if (lfs2_gstate_hasmovehere(&lfs2->gdisk, dir->pair) &&
- lfs2_tag_id(gmask) != 0 &&
- lfs2_tag_id(lfs2->gdisk.tag) <= lfs2_tag_id(gtag)) {
- // synthetic moves
- gdiff -= LFS2_MKTAG(0, 1, 0);
+ lfs2_tag_id(gmask) != 0) {
+ if (lfs2_tag_id(lfs2->gdisk.tag) == lfs2_tag_id(gtag)) {
+ return LFS2_ERR_NOENT;
+ } else if (lfs2_tag_id(lfs2->gdisk.tag) < lfs2_tag_id(gtag)) {
+ gdiff -= LFS2_MKTAG(0, 1, 0);
+ }
}
// iterate over dir block backwards (for faster lookups)
@@ -1438,32 +1473,46 @@ static int lfs2_dir_find_match(void *data,
return LFS2_CMP_EQ;
}
+// lfs2_dir_find tries to set path and id even if file is not found
+//
+// returns:
+// - 0 if file is found
+// - LFS2_ERR_NOENT if file or parent is not found
+// - LFS2_ERR_NOTDIR if parent is not a dir
static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir,
const char **path, uint16_t *id) {
// we reduce path to a single name if we can find it
const char *name = *path;
- if (id) {
- *id = 0x3ff;
- }
// default to root dir
lfs2_stag_t tag = LFS2_MKTAG(LFS2_TYPE_DIR, 0x3ff, 0);
dir->tail[0] = lfs2->root[0];
dir->tail[1] = lfs2->root[1];
+ // empty paths are not allowed
+ if (*name == '\0') {
+ return LFS2_ERR_INVAL;
+ }
+
while (true) {
nextname:
- // skip slashes
- name += strspn(name, "/");
+ // skip slashes if we're a directory
+ if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) {
+ name += strspn(name, "/");
+ }
lfs2_size_t namelen = strcspn(name, "/");
- // skip '.' and root '..'
- if ((namelen == 1 && memcmp(name, ".", 1) == 0) ||
- (namelen == 2 && memcmp(name, "..", 2) == 0)) {
+ // skip '.'
+ if (namelen == 1 && memcmp(name, ".", 1) == 0) {
name += namelen;
goto nextname;
}
+ // error on unmatched '..', trying to go above root?
+ if (namelen == 2 && memcmp(name, "..", 2) == 0) {
+ return LFS2_ERR_INVAL;
+ }
+
// skip if matched by '..' in name
const char *suffix = name + namelen;
lfs2_size_t sufflen;
@@ -1475,7 +1524,9 @@ nextname:
break;
}
- if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
+ if (sufflen == 1 && memcmp(suffix, ".", 1) == 0) {
+ // noop
+ } else if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
depth -= 1;
if (depth == 0) {
name = suffix + sufflen;
@@ -1489,14 +1540,14 @@ nextname:
}
// found path
- if (name[0] == '\0') {
+ if (*name == '\0') {
return tag;
}
// update what we've found so far
*path = name;
- // only continue if we hit a directory
+ // only continue if we're a directory
if (lfs2_tag_type3(tag) != LFS2_TYPE_DIR) {
return LFS2_ERR_NOTDIR;
}
@@ -1516,8 +1567,7 @@ nextname:
tag = lfs2_dir_fetchmatch(lfs2, dir, dir->tail,
LFS2_MKTAG(0x780, 0, 0),
LFS2_MKTAG(LFS2_TYPE_NAME, 0, namelen),
- // are we last name?
- (strchr(name, '/') == NULL) ? id : NULL,
+ id,
lfs2_dir_find_match, &(struct lfs2_dir_find_match){
lfs2, name, namelen});
if (tag < 0) {
@@ -2105,13 +2155,14 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir,
// And we cap at half a block to avoid degenerate cases with
// nearly-full metadata blocks.
//
+ lfs2_size_t metadata_max = (lfs2->cfg->metadata_max)
+ ? lfs2->cfg->metadata_max
+ : lfs2->cfg->block_size;
if (end - split < 0xff
&& size <= lfs2_min(
- lfs2->cfg->block_size - 40,
+ metadata_max - 40,
lfs2_alignup(
- (lfs2->cfg->metadata_max
- ? lfs2->cfg->metadata_max
- : lfs2->cfg->block_size)/2,
+ metadata_max/2,
lfs2->cfg->prog_size))) {
break;
}
@@ -2146,14 +2197,16 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir,
&& lfs2_pair_cmp(dir->pair, (const lfs2_block_t[2]){0, 1}) == 0) {
// oh no! we're writing too much to the superblock,
// should we expand?
- lfs2_ssize_t size = lfs2_fs_rawsize(lfs2);
+ lfs2_ssize_t size = lfs2_fs_size_(lfs2);
if (size < 0) {
return size;
}
- // do we have extra space? littlefs can't reclaim this space
- // by itself, so expand cautiously
- if ((lfs2_size_t)size < lfs2->block_count/2) {
+ // littlefs cannot reclaim expanded superblocks, so expand cautiously
+ //
+ // if our filesystem is more than ~88% full, don't expand, this is
+ // somewhat arbitrary
+ if (lfs2->block_count - size > lfs2->block_count/8) {
LFS2_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev);
int err = lfs2_dir_split(lfs2, dir, attrs, attrcount,
source, begin, end);
@@ -2166,7 +2219,8 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir,
// we can do, we'll error later if we've become frozen
LFS2_WARN("Unable to expand superblock");
} else {
- end = begin;
+ // duplicate the superblock entry into the new superblock
+ end = 1;
}
}
}
@@ -2312,7 +2366,8 @@ fixmlist:;
if (d->m.pair != pair) {
for (int i = 0; i < attrcount; i++) {
if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_DELETE &&
- d->id == lfs2_tag_id(attrs[i].tag)) {
+ d->id == lfs2_tag_id(attrs[i].tag) &&
+ d->type != LFS2_TYPE_DIR) {
d->m.pair[0] = LFS2_BLOCK_NULL;
d->m.pair[1] = LFS2_BLOCK_NULL;
} else if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_DELETE &&
@@ -2333,7 +2388,9 @@ fixmlist:;
while (d->id >= d->m.count && d->m.split) {
// we split and id is on tail now
- d->id -= d->m.count;
+ if (lfs2_pair_cmp(d->m.tail, lfs2->root) != 0) {
+ d->id -= d->m.count;
+ }
int err = lfs2_dir_fetch(lfs2, &d->m, d->m.tail);
if (err) {
return err;
@@ -2499,7 +2556,7 @@ static int lfs2_dir_orphaningcommit(lfs2_t *lfs2, lfs2_mdir_t *dir,
if (err != LFS2_ERR_NOENT) {
if (lfs2_gstate_hasorphans(&lfs2->gstate)) {
// next step, clean up orphans
- err = lfs2_fs_preporphans(lfs2, -hasparent);
+ err = lfs2_fs_preporphans(lfs2, -(int8_t)hasparent);
if (err) {
return err;
}
@@ -2564,7 +2621,7 @@ static int lfs2_dir_commit(lfs2_t *lfs2, lfs2_mdir_t *dir,
/// Top level directory operations ///
#ifndef LFS2_READONLY
-static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) {
+static int lfs2_mkdir_(lfs2_t *lfs2, const char *path) {
// deorphan if we haven't yet, needed at most once after poweron
int err = lfs2_fs_forceconsistency(lfs2);
if (err) {
@@ -2575,18 +2632,18 @@ static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) {
cwd.next = lfs2->mlist;
uint16_t id;
err = lfs2_dir_find(lfs2, &cwd.m, &path, &id);
- if (!(err == LFS2_ERR_NOENT && id != 0x3ff)) {
+ if (!(err == LFS2_ERR_NOENT && lfs2_path_islast(path))) {
return (err < 0) ? err : LFS2_ERR_EXIST;
}
// check that name fits
- lfs2_size_t nlen = strlen(path);
+ lfs2_size_t nlen = lfs2_path_namelen(path);
if (nlen > lfs2->name_max) {
return LFS2_ERR_NAMETOOLONG;
}
// build up new directory
- lfs2_alloc_ack(lfs2);
+ lfs2_alloc_ckpoint(lfs2);
lfs2_mdir_t dir;
err = lfs2_dir_alloc(lfs2, &dir);
if (err) {
@@ -2660,7 +2717,7 @@ static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) {
}
#endif
-static int lfs2_dir_rawopen(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) {
+static int lfs2_dir_open_(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) {
lfs2_stag_t tag = lfs2_dir_find(lfs2, &dir->m, &path, NULL);
if (tag < 0) {
return tag;
@@ -2704,14 +2761,14 @@ static int lfs2_dir_rawopen(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) {
return 0;
}
-static int lfs2_dir_rawclose(lfs2_t *lfs2, lfs2_dir_t *dir) {
+static int lfs2_dir_close_(lfs2_t *lfs2, lfs2_dir_t *dir) {
// remove from list of mdirs
lfs2_mlist_remove(lfs2, (struct lfs2_mlist *)dir);
return 0;
}
-static int lfs2_dir_rawread(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) {
+static int lfs2_dir_read_(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) {
memset(info, 0, sizeof(*info));
// special offset for '.' and '..'
@@ -2756,9 +2813,9 @@ static int lfs2_dir_rawread(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *inf
return true;
}
-static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) {
+static int lfs2_dir_seek_(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) {
// simply walk from head dir
- int err = lfs2_dir_rawrewind(lfs2, dir);
+ int err = lfs2_dir_rewind_(lfs2, dir);
if (err) {
return err;
}
@@ -2793,12 +2850,12 @@ static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) {
return 0;
}
-static lfs2_soff_t lfs2_dir_rawtell(lfs2_t *lfs2, lfs2_dir_t *dir) {
+static lfs2_soff_t lfs2_dir_tell_(lfs2_t *lfs2, lfs2_dir_t *dir) {
(void)lfs2;
return dir->pos;
}
-static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir) {
+static int lfs2_dir_rewind_(lfs2_t *lfs2, lfs2_dir_t *dir) {
// reload the head dir
int err = lfs2_dir_fetch(lfs2, &dir->m, dir->head);
if (err) {
@@ -3004,7 +3061,7 @@ static int lfs2_ctz_traverse(lfs2_t *lfs2,
/// Top level file operations ///
-static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file,
+static int lfs2_file_opencfg_(lfs2_t *lfs2, lfs2_file_t *file,
const char *path, int flags,
const struct lfs2_file_config *cfg) {
#ifndef LFS2_READONLY
@@ -3029,7 +3086,7 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file,
// allocate entry for file if it doesn't exist
lfs2_stag_t tag = lfs2_dir_find(lfs2, &file->m, &path, &file->id);
- if (tag < 0 && !(tag == LFS2_ERR_NOENT && file->id != 0x3ff)) {
+ if (tag < 0 && !(tag == LFS2_ERR_NOENT && lfs2_path_islast(path))) {
err = tag;
goto cleanup;
}
@@ -3049,8 +3106,14 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file,
goto cleanup;
}
+ // don't allow trailing slashes
+ if (lfs2_path_isdir(path)) {
+ err = LFS2_ERR_NOTDIR;
+ goto cleanup;
+ }
+
// check that name fits
- lfs2_size_t nlen = strlen(path);
+ lfs2_size_t nlen = lfs2_path_namelen(path);
if (nlen > lfs2->name_max) {
err = LFS2_ERR_NAMETOOLONG;
goto cleanup;
@@ -3166,22 +3229,22 @@ cleanup:
#ifndef LFS2_READONLY
file->flags |= LFS2_F_ERRED;
#endif
- lfs2_file_rawclose(lfs2, file);
+ lfs2_file_close_(lfs2, file);
return err;
}
#ifndef LFS2_NO_MALLOC
-static int lfs2_file_rawopen(lfs2_t *lfs2, lfs2_file_t *file,
+static int lfs2_file_open_(lfs2_t *lfs2, lfs2_file_t *file,
const char *path, int flags) {
static const struct lfs2_file_config defaults = {0};
- int err = lfs2_file_rawopencfg(lfs2, file, path, flags, &defaults);
+ int err = lfs2_file_opencfg_(lfs2, file, path, flags, &defaults);
return err;
}
#endif
-static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file) {
+static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file) {
#ifndef LFS2_READONLY
- int err = lfs2_file_rawsync(lfs2, file);
+ int err = lfs2_file_sync_(lfs2, file);
#else
int err = 0;
#endif
@@ -3272,7 +3335,7 @@ relocate:
#ifndef LFS2_READONLY
static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file) {
file->off = file->pos;
- lfs2_alloc_ack(lfs2);
+ lfs2_alloc_ckpoint(lfs2);
int err = lfs2_file_relocate(lfs2, file);
if (err) {
return err;
@@ -3364,7 +3427,7 @@ relocate:
}
#ifndef LFS2_READONLY
-static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) {
+static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file) {
if (file->flags & LFS2_F_ERRED) {
// it's not safe to do anything if our file errored
return 0;
@@ -3379,6 +3442,15 @@ static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) {
if ((file->flags & LFS2_F_DIRTY) &&
!lfs2_pair_isnull(file->m.pair)) {
+ // before we commit metadata, we need sync the disk to make sure
+ // data writes don't complete after metadata writes
+ if (!(file->flags & LFS2_F_INLINE)) {
+ err = lfs2_bd_sync(lfs2, &lfs2->pcache, &lfs2->rcache, false);
+ if (err) {
+ return err;
+ }
+ }
+
// update dir entry
uint16_t type;
const void *buffer;
@@ -3477,7 +3549,7 @@ static lfs2_ssize_t lfs2_file_flushedread(lfs2_t *lfs2, lfs2_file_t *file,
return size;
}
-static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file,
+static lfs2_ssize_t lfs2_file_read_(lfs2_t *lfs2, lfs2_file_t *file,
void *buffer, lfs2_size_t size) {
LFS2_ASSERT((file->flags & LFS2_O_RDONLY) == LFS2_O_RDONLY);
@@ -3502,11 +3574,7 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file,
lfs2_size_t nsize = size;
if ((file->flags & LFS2_F_INLINE) &&
- lfs2_max(file->pos+nsize, file->ctz.size) >
- lfs2_min(0x3fe, lfs2_min(
- lfs2->cfg->cache_size,
- (lfs2->cfg->metadata_max ?
- lfs2->cfg->metadata_max : lfs2->cfg->block_size) / 8))) {
+ lfs2_max(file->pos+nsize, file->ctz.size) > lfs2->inline_max) {
// inline file doesn't fit anymore
int err = lfs2_file_outline(lfs2, file);
if (err) {
@@ -3535,7 +3603,7 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file,
}
// extend file with new blocks
- lfs2_alloc_ack(lfs2);
+ lfs2_alloc_ckpoint(lfs2);
int err = lfs2_ctz_extend(lfs2, &file->cache, &lfs2->rcache,
file->block, file->pos,
&file->block, &file->off);
@@ -3578,13 +3646,13 @@ relocate:
data += diff;
nsize -= diff;
- lfs2_alloc_ack(lfs2);
+ lfs2_alloc_ckpoint(lfs2);
}
return size;
}
-static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file,
+static lfs2_ssize_t lfs2_file_write_(lfs2_t *lfs2, lfs2_file_t *file,
const void *buffer, lfs2_size_t size) {
LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY);
@@ -3628,25 +3696,19 @@ static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file,
}
#endif
-static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file,
+static lfs2_soff_t lfs2_file_seek_(lfs2_t *lfs2, lfs2_file_t *file,
lfs2_soff_t off, int whence) {
// find new pos
+ //
+ // fortunately for us, littlefs is limited to 31-bit file sizes, so we
+ // don't have to worry too much about integer overflow
lfs2_off_t npos = file->pos;
if (whence == LFS2_SEEK_SET) {
npos = off;
} else if (whence == LFS2_SEEK_CUR) {
- if ((lfs2_soff_t)file->pos + off < 0) {
- return LFS2_ERR_INVAL;
- } else {
- npos = file->pos + off;
- }
+ npos = file->pos + (lfs2_off_t)off;
} else if (whence == LFS2_SEEK_END) {
- lfs2_soff_t res = lfs2_file_rawsize(lfs2, file) + off;
- if (res < 0) {
- return LFS2_ERR_INVAL;
- } else {
- npos = res;
- }
+ npos = (lfs2_off_t)lfs2_file_size_(lfs2, file) + (lfs2_off_t)off;
}
if (npos > lfs2->file_max) {
@@ -3661,13 +3723,8 @@ static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file,
// if we're only reading and our new offset is still in the file's cache
// we can avoid flushing and needing to reread the data
- if (
-#ifndef LFS2_READONLY
- !(file->flags & LFS2_F_WRITING)
-#else
- true
-#endif
- ) {
+ if ((file->flags & LFS2_F_READING)
+ && file->off != lfs2->cfg->block_size) {
int oindex = lfs2_ctz_index(lfs2, &(lfs2_off_t){file->pos});
lfs2_off_t noff = npos;
int nindex = lfs2_ctz_index(lfs2, &noff);
@@ -3692,7 +3749,7 @@ static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file,
}
#ifndef LFS2_READONLY
-static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) {
+static int lfs2_file_truncate_(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) {
LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY);
if (size > LFS2_FILE_MAX) {
@@ -3700,15 +3757,12 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz
}
lfs2_off_t pos = file->pos;
- lfs2_off_t oldsize = lfs2_file_rawsize(lfs2, file);
+ lfs2_off_t oldsize = lfs2_file_size_(lfs2, file);
if (size < oldsize) {
// revert to inline file?
- if (size <= lfs2_min(0x3fe, lfs2_min(
- lfs2->cfg->cache_size,
- (lfs2->cfg->metadata_max ?
- lfs2->cfg->metadata_max : lfs2->cfg->block_size) / 8))) {
+ if (size <= lfs2->inline_max) {
// flush+seek to head
- lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET);
+ lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_SET);
if (res < 0) {
return (int)res;
}
@@ -3753,14 +3807,14 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz
}
} else if (size > oldsize) {
// flush+seek if not already at end
- lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_END);
+ lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_END);
if (res < 0) {
return (int)res;
}
// fill with zeros
while (file->pos < size) {
- res = lfs2_file_rawwrite(lfs2, file, &(uint8_t){0}, 1);
+ res = lfs2_file_write_(lfs2, file, &(uint8_t){0}, 1);
if (res < 0) {
return (int)res;
}
@@ -3768,7 +3822,7 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz
}
// restore pos
- lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, pos, LFS2_SEEK_SET);
+ lfs2_soff_t res = lfs2_file_seek_(lfs2, file, pos, LFS2_SEEK_SET);
if (res < 0) {
return (int)res;
}
@@ -3777,13 +3831,13 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz
}
#endif
-static lfs2_soff_t lfs2_file_rawtell(lfs2_t *lfs2, lfs2_file_t *file) {
+static lfs2_soff_t lfs2_file_tell_(lfs2_t *lfs2, lfs2_file_t *file) {
(void)lfs2;
return file->pos;
}
-static int lfs2_file_rawrewind(lfs2_t *lfs2, lfs2_file_t *file) {
- lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET);
+static int lfs2_file_rewind_(lfs2_t *lfs2, lfs2_file_t *file) {
+ lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_SET);
if (res < 0) {
return (int)res;
}
@@ -3791,7 +3845,7 @@ static int lfs2_file_rawrewind(lfs2_t *lfs2, lfs2_file_t *file) {
return 0;
}
-static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file) {
+static lfs2_soff_t lfs2_file_size_(lfs2_t *lfs2, lfs2_file_t *file) {
(void)lfs2;
#ifndef LFS2_READONLY
@@ -3805,18 +3859,24 @@ static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file) {
/// General fs operations ///
-static int lfs2_rawstat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) {
+static int lfs2_stat_(lfs2_t *lfs2, const char *path, struct lfs2_info *info) {
lfs2_mdir_t cwd;
lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL);
if (tag < 0) {
return (int)tag;
}
+ // only allow trailing slashes on dirs
+ if (strchr(path, '/') != NULL
+ && lfs2_tag_type3(tag) != LFS2_TYPE_DIR) {
+ return LFS2_ERR_NOTDIR;
+ }
+
return lfs2_dir_getinfo(lfs2, &cwd, lfs2_tag_id(tag), info);
}
#ifndef LFS2_READONLY
-static int lfs2_rawremove(lfs2_t *lfs2, const char *path) {
+static int lfs2_remove_(lfs2_t *lfs2, const char *path) {
// deorphan if we haven't yet, needed at most once after poweron
int err = lfs2_fs_forceconsistency(lfs2);
if (err) {
@@ -3895,7 +3955,7 @@ static int lfs2_rawremove(lfs2_t *lfs2, const char *path) {
#endif
#ifndef LFS2_READONLY
-static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath) {
+static int lfs2_rename_(lfs2_t *lfs2, const char *oldpath, const char *newpath) {
// deorphan if we haven't yet, needed at most once after poweron
int err = lfs2_fs_forceconsistency(lfs2);
if (err) {
@@ -3914,7 +3974,7 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath
uint16_t newid;
lfs2_stag_t prevtag = lfs2_dir_find(lfs2, &newcwd, &newpath, &newid);
if ((prevtag < 0 || lfs2_tag_id(prevtag) == 0x3ff) &&
- !(prevtag == LFS2_ERR_NOENT && newid != 0x3ff)) {
+ !(prevtag == LFS2_ERR_NOENT && lfs2_path_islast(newpath))) {
return (prevtag < 0) ? (int)prevtag : LFS2_ERR_INVAL;
}
@@ -3925,8 +3985,14 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath
struct lfs2_mlist prevdir;
prevdir.next = lfs2->mlist;
if (prevtag == LFS2_ERR_NOENT) {
+ // if we're a file, don't allow trailing slashes
+ if (lfs2_path_isdir(newpath)
+ && lfs2_tag_type3(oldtag) != LFS2_TYPE_DIR) {
+ return LFS2_ERR_NOTDIR;
+ }
+
// check that name fits
- lfs2_size_t nlen = strlen(newpath);
+ lfs2_size_t nlen = lfs2_path_namelen(newpath);
if (nlen > lfs2->name_max) {
return LFS2_ERR_NAMETOOLONG;
}
@@ -3938,7 +4004,9 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath
newoldid += 1;
}
} else if (lfs2_tag_type3(prevtag) != lfs2_tag_type3(oldtag)) {
- return LFS2_ERR_ISDIR;
+ return (lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR)
+ ? LFS2_ERR_ISDIR
+ : LFS2_ERR_NOTDIR;
} else if (samepair && newid == newoldid) {
// we're renaming to ourselves??
return 0;
@@ -3984,7 +4052,8 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath
{LFS2_MKTAG_IF(prevtag != LFS2_ERR_NOENT,
LFS2_TYPE_DELETE, newid, 0), NULL},
{LFS2_MKTAG(LFS2_TYPE_CREATE, newid, 0), NULL},
- {LFS2_MKTAG(lfs2_tag_type3(oldtag), newid, strlen(newpath)), newpath},
+ {LFS2_MKTAG(lfs2_tag_type3(oldtag),
+ newid, lfs2_path_namelen(newpath)), newpath},
{LFS2_MKTAG(LFS2_FROM_MOVE, newid, lfs2_tag_id(oldtag)), &oldcwd},
{LFS2_MKTAG_IF(samepair,
LFS2_TYPE_DELETE, newoldid, 0), NULL}));
@@ -4030,7 +4099,7 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath
}
#endif
-static lfs2_ssize_t lfs2_rawgetattr(lfs2_t *lfs2, const char *path,
+static lfs2_ssize_t lfs2_getattr_(lfs2_t *lfs2, const char *path,
uint8_t type, void *buffer, lfs2_size_t size) {
lfs2_mdir_t cwd;
lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL);
@@ -4088,7 +4157,7 @@ static int lfs2_commitattr(lfs2_t *lfs2, const char *path,
#endif
#ifndef LFS2_READONLY
-static int lfs2_rawsetattr(lfs2_t *lfs2, const char *path,
+static int lfs2_setattr_(lfs2_t *lfs2, const char *path,
uint8_t type, const void *buffer, lfs2_size_t size) {
if (size > lfs2->attr_max) {
return LFS2_ERR_NOSPC;
@@ -4099,13 +4168,28 @@ static int lfs2_rawsetattr(lfs2_t *lfs2, const char *path,
#endif
#ifndef LFS2_READONLY
-static int lfs2_rawremoveattr(lfs2_t *lfs2, const char *path, uint8_t type) {
+static int lfs2_removeattr_(lfs2_t *lfs2, const char *path, uint8_t type) {
return lfs2_commitattr(lfs2, path, type, NULL, 0x3ff);
}
#endif
/// Filesystem operations ///
+
+// compile time checks, see lfs2.h for why these limits exist
+#if LFS2_NAME_MAX > 1022
+#error "Invalid LFS2_NAME_MAX, must be <= 1022"
+#endif
+
+#if LFS2_FILE_MAX > 2147483647
+#error "Invalid LFS2_FILE_MAX, must be <= 2147483647"
+#endif
+
+#if LFS2_ATTR_MAX > 1022
+#error "Invalid LFS2_ATTR_MAX, must be <= 1022"
+#endif
+
+// common filesystem initialization
static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) {
lfs2->cfg = cfg;
lfs2->block_count = cfg->block_count; // May be 0
@@ -4126,6 +4210,14 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) {
// which littlefs currently does not support
LFS2_ASSERT((bool)0x80000000);
+ // check that the required io functions are provided
+ LFS2_ASSERT(lfs2->cfg->read != NULL);
+#ifndef LFS2_READONLY
+ LFS2_ASSERT(lfs2->cfg->prog != NULL);
+ LFS2_ASSERT(lfs2->cfg->erase != NULL);
+ LFS2_ASSERT(lfs2->cfg->sync != NULL);
+#endif
+
// validate that the lfs2-cfg sizes were initiated properly before
// performing any arithmetic logics with them
LFS2_ASSERT(lfs2->cfg->read_size != 0);
@@ -4153,6 +4245,23 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) {
// wear-leveling.
LFS2_ASSERT(lfs2->cfg->block_cycles != 0);
+ // check that compact_thresh makes sense
+ //
+ // metadata can't be compacted below block_size/2, and metadata can't
+ // exceed a block_size
+ LFS2_ASSERT(lfs2->cfg->compact_thresh == 0
+ || lfs2->cfg->compact_thresh >= lfs2->cfg->block_size/2);
+ LFS2_ASSERT(lfs2->cfg->compact_thresh == (lfs2_size_t)-1
+ || lfs2->cfg->compact_thresh <= lfs2->cfg->block_size);
+
+ // check that metadata_max is a multiple of read_size and prog_size,
+ // and a factor of the block_size
+ LFS2_ASSERT(!lfs2->cfg->metadata_max
+ || lfs2->cfg->metadata_max % lfs2->cfg->read_size == 0);
+ LFS2_ASSERT(!lfs2->cfg->metadata_max
+ || lfs2->cfg->metadata_max % lfs2->cfg->prog_size == 0);
+ LFS2_ASSERT(!lfs2->cfg->metadata_max
+ || lfs2->cfg->block_size % lfs2->cfg->metadata_max == 0);
// setup read cache
if (lfs2->cfg->read_buffer) {
@@ -4180,15 +4289,14 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) {
lfs2_cache_zero(lfs2, &lfs2->rcache);
lfs2_cache_zero(lfs2, &lfs2->pcache);
- // setup lookahead, must be multiple of 64-bits, 32-bit aligned
+ // setup lookahead buffer, note mount finishes initializing this after
+ // we establish a decent pseudo-random seed
LFS2_ASSERT(lfs2->cfg->lookahead_size > 0);
- LFS2_ASSERT(lfs2->cfg->lookahead_size % 8 == 0 &&
- (uintptr_t)lfs2->cfg->lookahead_buffer % 4 == 0);
if (lfs2->cfg->lookahead_buffer) {
- lfs2->free.buffer = lfs2->cfg->lookahead_buffer;
+ lfs2->lookahead.buffer = lfs2->cfg->lookahead_buffer;
} else {
- lfs2->free.buffer = lfs2_malloc(lfs2->cfg->lookahead_size);
- if (!lfs2->free.buffer) {
+ lfs2->lookahead.buffer = lfs2_malloc(lfs2->cfg->lookahead_size);
+ if (!lfs2->lookahead.buffer) {
err = LFS2_ERR_NOMEM;
goto cleanup;
}
@@ -4215,6 +4323,27 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) {
LFS2_ASSERT(lfs2->cfg->metadata_max <= lfs2->cfg->block_size);
+ LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1
+ || lfs2->cfg->inline_max <= lfs2->cfg->cache_size);
+ LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1
+ || lfs2->cfg->inline_max <= lfs2->attr_max);
+ LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1
+ || lfs2->cfg->inline_max <= ((lfs2->cfg->metadata_max)
+ ? lfs2->cfg->metadata_max
+ : lfs2->cfg->block_size)/8);
+ lfs2->inline_max = lfs2->cfg->inline_max;
+ if (lfs2->inline_max == (lfs2_size_t)-1) {
+ lfs2->inline_max = 0;
+ } else if (lfs2->inline_max == 0) {
+ lfs2->inline_max = lfs2_min(
+ lfs2->cfg->cache_size,
+ lfs2_min(
+ lfs2->attr_max,
+ ((lfs2->cfg->metadata_max)
+ ? lfs2->cfg->metadata_max
+ : lfs2->cfg->block_size)/8));
+ }
+
// setup default state
lfs2->root[0] = LFS2_BLOCK_NULL;
lfs2->root[1] = LFS2_BLOCK_NULL;
@@ -4245,7 +4374,7 @@ static int lfs2_deinit(lfs2_t *lfs2) {
}
if (!lfs2->cfg->lookahead_buffer) {
- lfs2_free(lfs2->free.buffer);
+ lfs2_free(lfs2->lookahead.buffer);
}
return 0;
@@ -4254,7 +4383,7 @@ static int lfs2_deinit(lfs2_t *lfs2) {
#ifndef LFS2_READONLY
-static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) {
+static int lfs2_format_(lfs2_t *lfs2, const struct lfs2_config *cfg) {
int err = 0;
{
err = lfs2_init(lfs2, cfg);
@@ -4265,12 +4394,12 @@ static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) {
LFS2_ASSERT(cfg->block_count != 0);
// create free lookahead
- memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size);
- lfs2->free.off = 0;
- lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size,
+ memset(lfs2->lookahead.buffer, 0, lfs2->cfg->lookahead_size);
+ lfs2->lookahead.start = 0;
+ lfs2->lookahead.size = lfs2_min(8*lfs2->cfg->lookahead_size,
lfs2->block_count);
- lfs2->free.i = 0;
- lfs2_alloc_ack(lfs2);
+ lfs2->lookahead.next = 0;
+ lfs2_alloc_ckpoint(lfs2);
// create root dir
lfs2_mdir_t root;
@@ -4321,7 +4450,31 @@ cleanup:
}
#endif
-static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
+struct lfs2_tortoise_t {
+ lfs2_block_t pair[2];
+ lfs2_size_t i;
+ lfs2_size_t period;
+};
+
+static int lfs2_tortoise_detectcycles(
+ const lfs2_mdir_t *dir, struct lfs2_tortoise_t *tortoise) {
+ // detect cycles with Brent's algorithm
+ if (lfs2_pair_issync(dir->tail, tortoise->pair)) {
+ LFS2_WARN("Cycle detected in tail list");
+ return LFS2_ERR_CORRUPT;
+ }
+ if (tortoise->i == tortoise->period) {
+ tortoise->pair[0] = dir->tail[0];
+ tortoise->pair[1] = dir->tail[1];
+ tortoise->i = 0;
+ tortoise->period *= 2;
+ }
+ tortoise->i += 1;
+
+ return LFS2_ERR_OK;
+}
+
+static int lfs2_mount_(lfs2_t *lfs2, const struct lfs2_config *cfg) {
int err = lfs2_init(lfs2, cfg);
if (err) {
return err;
@@ -4329,23 +4482,16 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
// scan directory blocks for superblock and any global updates
lfs2_mdir_t dir = {.tail = {0, 1}};
- lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL};
- lfs2_size_t tortoise_i = 1;
- lfs2_size_t tortoise_period = 1;
+ struct lfs2_tortoise_t tortoise = {
+ .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL},
+ .i = 1,
+ .period = 1,
+ };
while (!lfs2_pair_isnull(dir.tail)) {
- // detect cycles with Brent's algorithm
- if (lfs2_pair_issync(dir.tail, tortoise)) {
- LFS2_WARN("Cycle detected in tail list");
- err = LFS2_ERR_CORRUPT;
+ err = lfs2_tortoise_detectcycles(&dir, &tortoise);
+ if (err < 0) {
goto cleanup;
}
- if (tortoise_i == tortoise_period) {
- tortoise[0] = dir.tail[0];
- tortoise[1] = dir.tail[1];
- tortoise_i = 0;
- tortoise_period *= 2;
- }
- tortoise_i += 1;
// fetch next block in tail list
lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, &dir, dir.tail,
@@ -4394,6 +4540,7 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
// found older minor version? set an in-device only bit in the
// gstate so we know we need to rewrite the superblock before
// the first write
+ bool needssuperblock = false;
if (minor_version < lfs2_fs_disk_version_minor(lfs2)) {
LFS2_DEBUG("Found older minor version "
"v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
@@ -4401,10 +4548,11 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
minor_version,
lfs2_fs_disk_version_major(lfs2),
lfs2_fs_disk_version_minor(lfs2));
- // note this bit is reserved on disk, so fetching more gstate
- // will not interfere here
- lfs2_fs_prepsuperblock(lfs2, true);
+ needssuperblock = true;
}
+ // note this bit is reserved on disk, so fetching more gstate
+ // will not interfere here
+ lfs2_fs_prepsuperblock(lfs2, needssuperblock);
// check superblock configuration
if (superblock.name_max) {
@@ -4438,6 +4586,9 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
}
lfs2->attr_max = superblock.attr_max;
+
+ // we also need to update inline_max in case attr_max changed
+ lfs2->inline_max = lfs2_min(lfs2->inline_max, lfs2->attr_max);
}
// this is where we get the block_count from disk if block_count=0
@@ -4478,23 +4629,23 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
// setup free lookahead, to distribute allocations uniformly across
// boots, we start the allocator at a random location
- lfs2->free.off = lfs2->seed % lfs2->block_count;
+ lfs2->lookahead.start = lfs2->seed % lfs2->block_count;
lfs2_alloc_drop(lfs2);
return 0;
cleanup:
- lfs2_rawunmount(lfs2);
+ lfs2_unmount_(lfs2);
return err;
}
-static int lfs2_rawunmount(lfs2_t *lfs2) {
+static int lfs2_unmount_(lfs2_t *lfs2) {
return lfs2_deinit(lfs2);
}
/// Filesystem filesystem operations ///
-static int lfs2_fs_rawstat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) {
+static int lfs2_fs_stat_(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) {
// if the superblock is up-to-date, we must be on the most recent
// minor version of littlefs
if (!lfs2_gstate_needssuperblock(&lfs2->gstate)) {
@@ -4534,7 +4685,7 @@ static int lfs2_fs_rawstat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) {
return 0;
}
-int lfs2_fs_rawtraverse(lfs2_t *lfs2,
+int lfs2_fs_traverse_(lfs2_t *lfs2,
int (*cb)(void *data, lfs2_block_t block), void *data,
bool includeorphans) {
// iterate over metadata pairs
@@ -4553,22 +4704,17 @@ int lfs2_fs_rawtraverse(lfs2_t *lfs2,
}
#endif
- lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL};
- lfs2_size_t tortoise_i = 1;
- lfs2_size_t tortoise_period = 1;
+ struct lfs2_tortoise_t tortoise = {
+ .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL},
+ .i = 1,
+ .period = 1,
+ };
+ int err = LFS2_ERR_OK;
while (!lfs2_pair_isnull(dir.tail)) {
- // detect cycles with Brent's algorithm
- if (lfs2_pair_issync(dir.tail, tortoise)) {
- LFS2_WARN("Cycle detected in tail list");
+ err = lfs2_tortoise_detectcycles(&dir, &tortoise);
+ if (err < 0) {
return LFS2_ERR_CORRUPT;
}
- if (tortoise_i == tortoise_period) {
- tortoise[0] = dir.tail[0];
- tortoise[1] = dir.tail[1];
- tortoise_i = 0;
- tortoise_period *= 2;
- }
- tortoise_i += 1;
for (int i = 0; i < 2; i++) {
int err = cb(data, dir.tail[i]);
@@ -4647,22 +4793,17 @@ static int lfs2_fs_pred(lfs2_t *lfs2,
// iterate over all directory directory entries
pdir->tail[0] = 0;
pdir->tail[1] = 1;
- lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL};
- lfs2_size_t tortoise_i = 1;
- lfs2_size_t tortoise_period = 1;
+ struct lfs2_tortoise_t tortoise = {
+ .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL},
+ .i = 1,
+ .period = 1,
+ };
+ int err = LFS2_ERR_OK;
while (!lfs2_pair_isnull(pdir->tail)) {
- // detect cycles with Brent's algorithm
- if (lfs2_pair_issync(pdir->tail, tortoise)) {
- LFS2_WARN("Cycle detected in tail list");
+ err = lfs2_tortoise_detectcycles(pdir, &tortoise);
+ if (err < 0) {
return LFS2_ERR_CORRUPT;
}
- if (tortoise_i == tortoise_period) {
- tortoise[0] = pdir->tail[0];
- tortoise[1] = pdir->tail[1];
- tortoise_i = 0;
- tortoise_period *= 2;
- }
- tortoise_i += 1;
if (lfs2_pair_cmp(pdir->tail, pair) == 0) {
return 0;
@@ -4712,22 +4853,17 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2],
// use fetchmatch with callback to find pairs
parent->tail[0] = 0;
parent->tail[1] = 1;
- lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL};
- lfs2_size_t tortoise_i = 1;
- lfs2_size_t tortoise_period = 1;
+ struct lfs2_tortoise_t tortoise = {
+ .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL},
+ .i = 1,
+ .period = 1,
+ };
+ int err = LFS2_ERR_OK;
while (!lfs2_pair_isnull(parent->tail)) {
- // detect cycles with Brent's algorithm
- if (lfs2_pair_issync(parent->tail, tortoise)) {
- LFS2_WARN("Cycle detected in tail list");
- return LFS2_ERR_CORRUPT;
- }
- if (tortoise_i == tortoise_period) {
- tortoise[0] = parent->tail[0];
- tortoise[1] = parent->tail[1];
- tortoise_i = 0;
- tortoise_period *= 2;
+ err = lfs2_tortoise_detectcycles(parent, &tortoise);
+ if (err < 0) {
+ return err;
}
- tortoise_i += 1;
lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, parent, parent->tail,
LFS2_MKTAG(0x7ff, 0, 0x3ff),
@@ -4999,7 +5135,7 @@ static int lfs2_fs_forceconsistency(lfs2_t *lfs2) {
#endif
#ifndef LFS2_READONLY
-static int lfs2_fs_rawmkconsistent(lfs2_t *lfs2) {
+static int lfs2_fs_mkconsistent_(lfs2_t *lfs2) {
// lfs2_fs_forceconsistency does most of the work here
int err = lfs2_fs_forceconsistency(lfs2);
if (err) {
@@ -5035,9 +5171,9 @@ static int lfs2_fs_size_count(void *p, lfs2_block_t block) {
return 0;
}
-static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) {
+static lfs2_ssize_t lfs2_fs_size_(lfs2_t *lfs2) {
lfs2_size_t size = 0;
- int err = lfs2_fs_rawtraverse(lfs2, lfs2_fs_size_count, &size, false);
+ int err = lfs2_fs_traverse_(lfs2, lfs2_fs_size_count, &size, false);
if (err) {
return err;
}
@@ -5045,8 +5181,59 @@ static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) {
return size;
}
+// explicit garbage collection
#ifndef LFS2_READONLY
-static int lfs2_fs_rawgrow(lfs2_t *lfs2, lfs2_size_t block_count) {
+static int lfs2_fs_gc_(lfs2_t *lfs2) {
+ // force consistency, even if we're not necessarily going to write,
+ // because this function is supposed to take care of janitorial work
+ // isn't it?
+ int err = lfs2_fs_forceconsistency(lfs2);
+ if (err) {
+ return err;
+ }
+
+ // try to compact metadata pairs, note we can't really accomplish
+ // anything if compact_thresh doesn't at least leave a prog_size
+ // available
+ if (lfs2->cfg->compact_thresh
+ < lfs2->cfg->block_size - lfs2->cfg->prog_size) {
+ // iterate over all mdirs
+ lfs2_mdir_t mdir = {.tail = {0, 1}};
+ while (!lfs2_pair_isnull(mdir.tail)) {
+ err = lfs2_dir_fetch(lfs2, &mdir, mdir.tail);
+ if (err) {
+ return err;
+ }
+
+ // not erased? exceeds our compaction threshold?
+ if (!mdir.erased || ((lfs2->cfg->compact_thresh == 0)
+ ? mdir.off > lfs2->cfg->block_size - lfs2->cfg->block_size/8
+ : mdir.off > lfs2->cfg->compact_thresh)) {
+ // the easiest way to trigger a compaction is to mark
+ // the mdir as unerased and add an empty commit
+ mdir.erased = false;
+ err = lfs2_dir_commit(lfs2, &mdir, NULL, 0);
+ if (err) {
+ return err;
+ }
+ }
+ }
+ }
+
+ // try to populate the lookahead buffer, unless it's already full
+ if (lfs2->lookahead.size < 8*lfs2->cfg->lookahead_size) {
+ err = lfs2_alloc_scan(lfs2);
+ if (err) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef LFS2_READONLY
+static int lfs2_fs_grow_(lfs2_t *lfs2, lfs2_size_t block_count) {
// shrinking is not supported
LFS2_ASSERT(block_count >= lfs2->block_count);
@@ -5451,10 +5638,10 @@ static int lfs21_mount(lfs2_t *lfs2, struct lfs21 *lfs21,
lfs2->lfs21->root[1] = LFS2_BLOCK_NULL;
// setup free lookahead
- lfs2->free.off = 0;
- lfs2->free.size = 0;
- lfs2->free.i = 0;
- lfs2_alloc_ack(lfs2);
+ lfs2->lookahead.start = 0;
+ lfs2->lookahead.size = 0;
+ lfs2->lookahead.next = 0;
+ lfs2_alloc_ckpoint(lfs2);
// load superblock
lfs21_dir_t dir;
@@ -5505,7 +5692,7 @@ static int lfs21_unmount(lfs2_t *lfs2) {
}
/// v1 migration ///
-static int lfs2_rawmigrate(lfs2_t *lfs2, const struct lfs2_config *cfg) {
+static int lfs2_migrate_(lfs2_t *lfs2, const struct lfs2_config *cfg) {
struct lfs21 lfs21;
// Indeterminate filesystem size not allowed for migration.
@@ -5759,7 +5946,7 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) {
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32", "
- ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
+ ".block_cycles=%"PRId32", .cache_size=%"PRIu32", "
".lookahead_size=%"PRIu32", .read_buffer=%p, "
".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", "
@@ -5772,7 +5959,7 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) {
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
- err = lfs2_rawformat(lfs2, cfg);
+ err = lfs2_format_(lfs2, cfg);
LFS2_TRACE("lfs2_format -> %d", err);
LFS2_UNLOCK(cfg);
@@ -5789,7 +5976,7 @@ int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32", "
- ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
+ ".block_cycles=%"PRId32", .cache_size=%"PRIu32", "
".lookahead_size=%"PRIu32", .read_buffer=%p, "
".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", "
@@ -5802,7 +5989,7 @@ int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) {
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
- err = lfs2_rawmount(lfs2, cfg);
+ err = lfs2_mount_(lfs2, cfg);
LFS2_TRACE("lfs2_mount -> %d", err);
LFS2_UNLOCK(cfg);
@@ -5816,7 +6003,7 @@ int lfs2_unmount(lfs2_t *lfs2) {
}
LFS2_TRACE("lfs2_unmount(%p)", (void*)lfs2);
- err = lfs2_rawunmount(lfs2);
+ err = lfs2_unmount_(lfs2);
LFS2_TRACE("lfs2_unmount -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5831,7 +6018,7 @@ int lfs2_remove(lfs2_t *lfs2, const char *path) {
}
LFS2_TRACE("lfs2_remove(%p, \"%s\")", (void*)lfs2, path);
- err = lfs2_rawremove(lfs2, path);
+ err = lfs2_remove_(lfs2, path);
LFS2_TRACE("lfs2_remove -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5847,7 +6034,7 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) {
}
LFS2_TRACE("lfs2_rename(%p, \"%s\", \"%s\")", (void*)lfs2, oldpath, newpath);
- err = lfs2_rawrename(lfs2, oldpath, newpath);
+ err = lfs2_rename_(lfs2, oldpath, newpath);
LFS2_TRACE("lfs2_rename -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5862,7 +6049,7 @@ int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) {
}
LFS2_TRACE("lfs2_stat(%p, \"%s\", %p)", (void*)lfs2, path, (void*)info);
- err = lfs2_rawstat(lfs2, path, info);
+ err = lfs2_stat_(lfs2, path, info);
LFS2_TRACE("lfs2_stat -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5878,7 +6065,7 @@ lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path,
LFS2_TRACE("lfs2_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")",
(void*)lfs2, path, type, buffer, size);
- lfs2_ssize_t res = lfs2_rawgetattr(lfs2, path, type, buffer, size);
+ lfs2_ssize_t res = lfs2_getattr_(lfs2, path, type, buffer, size);
LFS2_TRACE("lfs2_getattr -> %"PRId32, res);
LFS2_UNLOCK(lfs2->cfg);
@@ -5895,7 +6082,7 @@ int lfs2_setattr(lfs2_t *lfs2, const char *path,
LFS2_TRACE("lfs2_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")",
(void*)lfs2, path, type, buffer, size);
- err = lfs2_rawsetattr(lfs2, path, type, buffer, size);
+ err = lfs2_setattr_(lfs2, path, type, buffer, size);
LFS2_TRACE("lfs2_setattr -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5911,7 +6098,7 @@ int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type) {
}
LFS2_TRACE("lfs2_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs2, path, type);
- err = lfs2_rawremoveattr(lfs2, path, type);
+ err = lfs2_removeattr_(lfs2, path, type);
LFS2_TRACE("lfs2_removeattr -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5926,10 +6113,10 @@ int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags)
return err;
}
LFS2_TRACE("lfs2_file_open(%p, %p, \"%s\", %x)",
- (void*)lfs2, (void*)file, path, flags);
+ (void*)lfs2, (void*)file, path, (unsigned)flags);
LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- err = lfs2_file_rawopen(lfs2, file, path, flags);
+ err = lfs2_file_open_(lfs2, file, path, flags);
LFS2_TRACE("lfs2_file_open -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5946,11 +6133,11 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file,
}
LFS2_TRACE("lfs2_file_opencfg(%p, %p, \"%s\", %x, %p {"
".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})",
- (void*)lfs2, (void*)file, path, flags,
+ (void*)lfs2, (void*)file, path, (unsigned)flags,
(void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count);
LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- err = lfs2_file_rawopencfg(lfs2, file, path, flags, cfg);
+ err = lfs2_file_opencfg_(lfs2, file, path, flags, cfg);
LFS2_TRACE("lfs2_file_opencfg -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5965,7 +6152,7 @@ int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file) {
LFS2_TRACE("lfs2_file_close(%p, %p)", (void*)lfs2, (void*)file);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- err = lfs2_file_rawclose(lfs2, file);
+ err = lfs2_file_close_(lfs2, file);
LFS2_TRACE("lfs2_file_close -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5981,7 +6168,7 @@ int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file) {
LFS2_TRACE("lfs2_file_sync(%p, %p)", (void*)lfs2, (void*)file);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- err = lfs2_file_rawsync(lfs2, file);
+ err = lfs2_file_sync_(lfs2, file);
LFS2_TRACE("lfs2_file_sync -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -5999,7 +6186,7 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file,
(void*)lfs2, (void*)file, buffer, size);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- lfs2_ssize_t res = lfs2_file_rawread(lfs2, file, buffer, size);
+ lfs2_ssize_t res = lfs2_file_read_(lfs2, file, buffer, size);
LFS2_TRACE("lfs2_file_read -> %"PRId32, res);
LFS2_UNLOCK(lfs2->cfg);
@@ -6017,7 +6204,7 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file,
(void*)lfs2, (void*)file, buffer, size);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- lfs2_ssize_t res = lfs2_file_rawwrite(lfs2, file, buffer, size);
+ lfs2_ssize_t res = lfs2_file_write_(lfs2, file, buffer, size);
LFS2_TRACE("lfs2_file_write -> %"PRId32, res);
LFS2_UNLOCK(lfs2->cfg);
@@ -6035,7 +6222,7 @@ lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file,
(void*)lfs2, (void*)file, off, whence);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, off, whence);
+ lfs2_soff_t res = lfs2_file_seek_(lfs2, file, off, whence);
LFS2_TRACE("lfs2_file_seek -> %"PRId32, res);
LFS2_UNLOCK(lfs2->cfg);
@@ -6052,7 +6239,7 @@ int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) {
(void*)lfs2, (void*)file, size);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- err = lfs2_file_rawtruncate(lfs2, file, size);
+ err = lfs2_file_truncate_(lfs2, file, size);
LFS2_TRACE("lfs2_file_truncate -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6068,7 +6255,7 @@ lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file) {
LFS2_TRACE("lfs2_file_tell(%p, %p)", (void*)lfs2, (void*)file);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- lfs2_soff_t res = lfs2_file_rawtell(lfs2, file);
+ lfs2_soff_t res = lfs2_file_tell_(lfs2, file);
LFS2_TRACE("lfs2_file_tell -> %"PRId32, res);
LFS2_UNLOCK(lfs2->cfg);
@@ -6082,7 +6269,7 @@ int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file) {
}
LFS2_TRACE("lfs2_file_rewind(%p, %p)", (void*)lfs2, (void*)file);
- err = lfs2_file_rawrewind(lfs2, file);
+ err = lfs2_file_rewind_(lfs2, file);
LFS2_TRACE("lfs2_file_rewind -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6097,9 +6284,9 @@ lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file) {
LFS2_TRACE("lfs2_file_size(%p, %p)", (void*)lfs2, (void*)file);
LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file));
- lfs2_soff_t res = lfs2_file_rawsize(lfs2, file);
+ lfs2_soff_t res = lfs2_file_size_(lfs2, file);
- LFS2_TRACE("lfs2_file_size -> %"PRId32, res);
+ LFS2_TRACE("lfs2_file_size -> %"PRIu32, res);
LFS2_UNLOCK(lfs2->cfg);
return res;
}
@@ -6112,7 +6299,7 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) {
}
LFS2_TRACE("lfs2_mkdir(%p, \"%s\")", (void*)lfs2, path);
- err = lfs2_rawmkdir(lfs2, path);
+ err = lfs2_mkdir_(lfs2, path);
LFS2_TRACE("lfs2_mkdir -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6128,7 +6315,7 @@ int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) {
LFS2_TRACE("lfs2_dir_open(%p, %p, \"%s\")", (void*)lfs2, (void*)dir, path);
LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)dir));
- err = lfs2_dir_rawopen(lfs2, dir, path);
+ err = lfs2_dir_open_(lfs2, dir, path);
LFS2_TRACE("lfs2_dir_open -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6142,7 +6329,7 @@ int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir) {
}
LFS2_TRACE("lfs2_dir_close(%p, %p)", (void*)lfs2, (void*)dir);
- err = lfs2_dir_rawclose(lfs2, dir);
+ err = lfs2_dir_close_(lfs2, dir);
LFS2_TRACE("lfs2_dir_close -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6157,7 +6344,7 @@ int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) {
LFS2_TRACE("lfs2_dir_read(%p, %p, %p)",
(void*)lfs2, (void*)dir, (void*)info);
- err = lfs2_dir_rawread(lfs2, dir, info);
+ err = lfs2_dir_read_(lfs2, dir, info);
LFS2_TRACE("lfs2_dir_read -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6172,7 +6359,7 @@ int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) {
LFS2_TRACE("lfs2_dir_seek(%p, %p, %"PRIu32")",
(void*)lfs2, (void*)dir, off);
- err = lfs2_dir_rawseek(lfs2, dir, off);
+ err = lfs2_dir_seek_(lfs2, dir, off);
LFS2_TRACE("lfs2_dir_seek -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6186,7 +6373,7 @@ lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir) {
}
LFS2_TRACE("lfs2_dir_tell(%p, %p)", (void*)lfs2, (void*)dir);
- lfs2_soff_t res = lfs2_dir_rawtell(lfs2, dir);
+ lfs2_soff_t res = lfs2_dir_tell_(lfs2, dir);
LFS2_TRACE("lfs2_dir_tell -> %"PRId32, res);
LFS2_UNLOCK(lfs2->cfg);
@@ -6200,7 +6387,7 @@ int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir) {
}
LFS2_TRACE("lfs2_dir_rewind(%p, %p)", (void*)lfs2, (void*)dir);
- err = lfs2_dir_rawrewind(lfs2, dir);
+ err = lfs2_dir_rewind_(lfs2, dir);
LFS2_TRACE("lfs2_dir_rewind -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6214,7 +6401,7 @@ int lfs2_fs_stat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) {
}
LFS2_TRACE("lfs2_fs_stat(%p, %p)", (void*)lfs2, (void*)fsinfo);
- err = lfs2_fs_rawstat(lfs2, fsinfo);
+ err = lfs2_fs_stat_(lfs2, fsinfo);
LFS2_TRACE("lfs2_fs_stat -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6228,7 +6415,7 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2) {
}
LFS2_TRACE("lfs2_fs_size(%p)", (void*)lfs2);
- lfs2_ssize_t res = lfs2_fs_rawsize(lfs2);
+ lfs2_ssize_t res = lfs2_fs_size_(lfs2);
LFS2_TRACE("lfs2_fs_size -> %"PRId32, res);
LFS2_UNLOCK(lfs2->cfg);
@@ -6243,7 +6430,7 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data)
LFS2_TRACE("lfs2_fs_traverse(%p, %p, %p)",
(void*)lfs2, (void*)(uintptr_t)cb, data);
- err = lfs2_fs_rawtraverse(lfs2, cb, data, true);
+ err = lfs2_fs_traverse_(lfs2, cb, data, true);
LFS2_TRACE("lfs2_fs_traverse -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6251,32 +6438,32 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data)
}
#ifndef LFS2_READONLY
-int lfs2_fs_gc(lfs2_t *lfs2) {
+int lfs2_fs_mkconsistent(lfs2_t *lfs2) {
int err = LFS2_LOCK(lfs2->cfg);
if (err) {
return err;
}
- LFS2_TRACE("lfs2_fs_gc(%p)", (void*)lfs2);
+ LFS2_TRACE("lfs2_fs_mkconsistent(%p)", (void*)lfs2);
- err = lfs2_fs_rawgc(lfs2);
+ err = lfs2_fs_mkconsistent_(lfs2);
- LFS2_TRACE("lfs2_fs_gc -> %d", err);
+ LFS2_TRACE("lfs2_fs_mkconsistent -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
return err;
}
#endif
#ifndef LFS2_READONLY
-int lfs2_fs_mkconsistent(lfs2_t *lfs2) {
+int lfs2_fs_gc(lfs2_t *lfs2) {
int err = LFS2_LOCK(lfs2->cfg);
if (err) {
return err;
}
- LFS2_TRACE("lfs2_fs_mkconsistent(%p)", (void*)lfs2);
+ LFS2_TRACE("lfs2_fs_gc(%p)", (void*)lfs2);
- err = lfs2_fs_rawmkconsistent(lfs2);
+ err = lfs2_fs_gc_(lfs2);
- LFS2_TRACE("lfs2_fs_mkconsistent -> %d", err);
+ LFS2_TRACE("lfs2_fs_gc -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
return err;
}
@@ -6290,7 +6477,7 @@ int lfs2_fs_grow(lfs2_t *lfs2, lfs2_size_t block_count) {
}
LFS2_TRACE("lfs2_fs_grow(%p, %"PRIu32")", (void*)lfs2, block_count);
- err = lfs2_fs_rawgrow(lfs2, block_count);
+ err = lfs2_fs_grow_(lfs2, block_count);
LFS2_TRACE("lfs2_fs_grow -> %d", err);
LFS2_UNLOCK(lfs2->cfg);
@@ -6308,7 +6495,7 @@ int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) {
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32", "
- ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
+ ".block_cycles=%"PRId32", .cache_size=%"PRIu32", "
".lookahead_size=%"PRIu32", .read_buffer=%p, "
".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", "
@@ -6321,7 +6508,7 @@ int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) {
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
- err = lfs2_rawmigrate(lfs2, cfg);
+ err = lfs2_migrate_(lfs2, cfg);
LFS2_TRACE("lfs2_migrate -> %d", err);
LFS2_UNLOCK(cfg);
diff --git a/lib/littlefs/lfs2.h b/lib/littlefs/lfs2.h
index 4c426fc4c7..f503fd00bc 100644
--- a/lib/littlefs/lfs2.h
+++ b/lib/littlefs/lfs2.h
@@ -21,7 +21,7 @@ extern "C"
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
-#define LFS2_VERSION 0x00020008
+#define LFS2_VERSION 0x0002000a
#define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16))
#define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0))
@@ -52,16 +52,15 @@ typedef uint32_t lfs2_block_t;
#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 lfs2_file_seek, lfs2_file_size, and lfs2_file_tell will return
-// incorrect values due to using signed integers. Stored in superblock and
-// must be respected by other littlefs drivers.
+// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be
+// respected by other littlefs drivers.
#ifndef LFS2_FILE_MAX
#define LFS2_FILE_MAX 2147483647
#endif
// Maximum size of custom attributes in bytes, may be redefined, but there is
-// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022.
+// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022. Stored
+// in superblock and must be respected by other littlefs drivers.
#ifndef LFS2_ATTR_MAX
#define LFS2_ATTR_MAX 1022
#endif
@@ -205,7 +204,8 @@ struct lfs2_config {
// program sizes.
lfs2_size_t block_size;
- // Number of erasable blocks on the device.
+ // Number of erasable blocks on the device. Defaults to block_count stored
+ // on disk when zero.
lfs2_size_t block_count;
// Number of erase cycles before littlefs evicts metadata logs and moves
@@ -226,9 +226,20 @@ struct lfs2_config {
// 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.
+ // can track 8 blocks.
lfs2_size_t lookahead_size;
+ // Threshold for metadata compaction during lfs2_fs_gc in bytes. Metadata
+ // pairs that exceed this threshold will be compacted during lfs2_fs_gc.
+ // Defaults to ~88% block_size when zero, though the default may change
+ // in the future.
+ //
+ // Note this only affects lfs2_fs_gc. Normal compactions still only occur
+ // when full.
+ //
+ // Set to -1 to disable metadata compaction during lfs2_fs_gc.
+ lfs2_size_t compact_thresh;
+
// Optional statically allocated read buffer. Must be cache_size.
// By default lfs2_malloc is used to allocate this buffer.
void *read_buffer;
@@ -237,25 +248,24 @@ struct lfs2_config {
// By default lfs2_malloc is used to allocate this buffer.
void *prog_buffer;
- // Optional statically allocated lookahead buffer. Must be lookahead_size
- // and aligned to a 32-bit boundary. By default lfs2_malloc is used to
- // allocate this buffer.
+ // Optional statically allocated lookahead buffer. Must be lookahead_size.
+ // By default lfs2_malloc is used to allocate this buffer.
void *lookahead_buffer;
// 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 LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX when zero. Stored in
- // superblock and must be respected by other littlefs drivers.
+ // the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX or name_max stored on
+ // disk when zero.
lfs2_size_t name_max;
// Optional upper limit on files in bytes. No downside for larger files
- // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX when zero. Stored
- // in superblock and must be respected by other littlefs drivers.
+ // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX or file_max stored
+ // on disk when zero.
lfs2_size_t file_max;
// Optional upper limit on custom attributes in bytes. No downside for
// larger attributes size but must be <= LFS2_ATTR_MAX. Defaults to
- // LFS2_ATTR_MAX when zero.
+ // LFS2_ATTR_MAX or attr_max stored on disk when zero.
lfs2_size_t attr_max;
// Optional upper limit on total space given to metadata pairs in bytes. On
@@ -264,6 +274,15 @@ struct lfs2_config {
// Defaults to block_size when zero.
lfs2_size_t metadata_max;
+ // Optional upper limit on inlined files in bytes. Inlined files live in
+ // metadata and decrease storage requirements, but may be limited to
+ // improve metadata-related performance. Must be <= cache_size, <=
+ // attr_max, and <= block_size/8. Defaults to the largest possible
+ // inline_max when zero.
+ //
+ // Set to -1 to disable inlined files.
+ lfs2_size_t inline_max;
+
#ifdef LFS2_MULTIVERSION
// On-disk version to use when writing in the form of 16-bit major version
// + 16-bit minor version. This limiting metadata to what is supported by
@@ -430,19 +449,20 @@ typedef struct lfs2 {
lfs2_gstate_t gdisk;
lfs2_gstate_t gdelta;
- struct lfs2_free {
- lfs2_block_t off;
+ struct lfs2_lookahead {
+ lfs2_block_t start;
lfs2_block_t size;
- lfs2_block_t i;
- lfs2_block_t ack;
- uint32_t *buffer;
- } free;
+ lfs2_block_t next;
+ lfs2_block_t ckpoint;
+ uint8_t *buffer;
+ } lookahead;
const struct lfs2_config *cfg;
lfs2_size_t block_count;
lfs2_size_t name_max;
lfs2_size_t file_max;
lfs2_size_t attr_max;
+ lfs2_size_t inline_max;
#ifdef LFS2_MIGRATE
struct lfs21 *lfs21;
@@ -712,18 +732,6 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2);
// Returns a negative error code on failure.
int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data);
-// Attempt to proactively find free blocks
-//
-// Calling this function is not required, but may allowing the offloading of
-// the expensive block allocation scan to a less time-critical code path.
-//
-// Note: littlefs currently does not persist any found free blocks to disk.
-// This may change in the future.
-//
-// Returns a negative error code on failure. Finding no free blocks is
-// not an error.
-int lfs2_fs_gc(lfs2_t *lfs2);
-
#ifndef LFS2_READONLY
// Attempt to make the filesystem consistent and ready for writing
//
@@ -737,6 +745,24 @@ int lfs2_fs_mkconsistent(lfs2_t *lfs2);
#endif
#ifndef LFS2_READONLY
+// Attempt any janitorial work
+//
+// This currently:
+// 1. Calls mkconsistent if not already consistent
+// 2. Compacts metadata > compact_thresh
+// 3. Populates the block allocator
+//
+// Though additional janitorial work may be added in the future.
+//
+// Calling this function is not required, but may allow the offloading of
+// expensive janitorial work to a less time-critical code path.
+//
+// Returns a negative error code on failure. Accomplishing nothing is not
+// an error.
+int lfs2_fs_gc(lfs2_t *lfs2);
+#endif
+
+#ifndef LFS2_READONLY
// Grows the filesystem to a new size, updating the superblock with the new
// block count.
//
diff --git a/lib/littlefs/lfs2_util.c b/lib/littlefs/lfs2_util.c
index c9850e7886..4fe7e5340c 100644
--- a/lib/littlefs/lfs2_util.c
+++ b/lib/littlefs/lfs2_util.c
@@ -11,6 +11,8 @@
#ifndef LFS2_CONFIG
+// If user provides their own CRC impl we don't need this
+#ifndef LFS2_CRC
// Software CRC implementation with small lookup table
uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = {
@@ -29,6 +31,7 @@ uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) {
return crc;
}
+#endif
#endif
diff --git a/lib/littlefs/lfs2_util.h b/lib/littlefs/lfs2_util.h
index dd2cbcc106..48d9f4c572 100644
--- a/lib/littlefs/lfs2_util.h
+++ b/lib/littlefs/lfs2_util.h
@@ -8,6 +8,9 @@
#ifndef LFS2_UTIL_H
#define LFS2_UTIL_H
+#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x)
+#define LFS2_STRINGIZE2(x) #x
+
// Users can override lfs2_util.h with their own configuration by defining
// LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h).
//
@@ -15,11 +18,26 @@
// provided by the config file. To start, I would suggest copying lfs2_util.h
// and modifying as needed.
#ifdef LFS2_CONFIG
-#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x)
-#define LFS2_STRINGIZE2(x) #x
#include LFS2_STRINGIZE(LFS2_CONFIG)
#else
+// Alternatively, users can provide a header file which defines
+// macros and other things consumed by littlefs.
+//
+// For example, provide my_defines.h, which contains
+// something like:
+//
+// #include <stddef.h>
+// extern void *my_malloc(size_t sz);
+// #define LFS2_MALLOC(sz) my_malloc(sz)
+//
+// And build littlefs with the header by defining LFS2_DEFINES.
+// (-DLFS2_DEFINES=my_defines.h)
+
+#ifdef LFS2_DEFINES
+#include LFS2_STRINGIZE(LFS2_DEFINES)
+#endif
+
// System includes
#include <stdint.h>
#include <stdbool.h>
@@ -212,12 +230,22 @@ static inline uint32_t lfs2_tobe32(uint32_t a) {
}
// Calculate CRC-32 with polynomial = 0x04c11db7
+#ifdef LFS2_CRC
+uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) {
+ return LFS2_CRC(crc, buffer, size)
+}
+#else
uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size);
+#endif
// Allocate memory, only used if buffers are not provided to littlefs
-// Note, memory must be 64-bit aligned
+//
+// littlefs current has no alignment requirements, as it only allocates
+// byte-level buffers.
static inline void *lfs2_malloc(size_t size) {
-#ifndef LFS2_NO_MALLOC
+#if defined(LFS2_MALLOC)
+ return LFS2_MALLOC(size);
+#elif !defined(LFS2_NO_MALLOC)
return malloc(size);
#else
(void)size;
@@ -227,7 +255,9 @@ static inline void *lfs2_malloc(size_t size) {
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs2_free(void *p) {
-#ifndef LFS2_NO_MALLOC
+#if defined(LFS2_FREE)
+ LFS2_FREE(p);
+#elif !defined(LFS2_NO_MALLOC)
free(p);
#else
(void)p;
diff --git a/tests/extmod/vfs_lfs.py b/tests/extmod/vfs_lfs.py
index 3ad57fd9c3..40d58e9c9f 100644
--- a/tests/extmod/vfs_lfs.py
+++ b/tests/extmod/vfs_lfs.py
@@ -136,7 +136,7 @@ def test(bdev, vfs_class):
print(fs.getcwd())
fs.chdir("../testdir")
print(fs.getcwd())
- fs.chdir("../..")
+ fs.chdir("..")
print(fs.getcwd())
fs.chdir(".//testdir")
print(fs.getcwd())