Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Haster <chaster@utexas.edu>2019-01-13 20:08:42 +0300
committerChristopher Haster <chaster@utexas.edu>2019-01-23 05:59:59 +0300
commite1f9d2bc09b194ef9a262b54513fb6cd7a626dee (patch)
tree93ac9d97b2d023c9ad502c46784315b82f35eb2d
parent51b2c7e4b64a692d3c71396e1cf2230245516f76 (diff)
Added support for RAM-independent reading of inline files
One of the new features in LittleFS is "inline files", which is the inlining of small files in the parent directory. Inline files have a big limitation in that they no longer have a dedicated scratch area to write out data before commit-time. This is fine as long as inline files are small enough to fit in RAM. However, this dependency on RAM creates an uncomfortable situation for portability, with larger devices able to create larger files than smaller devices. This problem is especially important on embedded systems, where RAM is at a premium. Recently, I realized this RAM requirement is necessary for _writing_ inline files, but not for _reading_ inline files. By allowing fetches of specific slices of inline files it's possible to read inline files without the RAM to back it. However however, this creates a conflict with COW semantics. Normally, when a file is open twice, it is referenced by a COW data structure that can be updated independently. Inlines files that fit in RAM also allows independent updates, but the moment an inline file can't fit in RAM, any updates to that directory block could corrupt open files referencing the inline file. The fact that this behaviour is only inconsistent for inline files created on a different device with more RAM creates a potential nightmare for user experience. Fortunately, there is a workaround for this. When we are commiting to a directory, any open files needs to live in a COW structure or in RAM. While we could move large inline files to COW structures at open time, this would break the separation of read/write operations and could lead to write errors at read time (ie ENOSPC). But since this is only an issue for commits, we can defer the move to a COW structure to any commits to that directory. This means when committing to a directory we need to find any _open_ large inline files and evict them from the directory, leaving the file with a new COW structure even if it was opened read only. While complicated, the end result is inline files that can use the MAX RAM that is available, but can be read with MIN RAM, even with multiple write operations happening to the underlying directory block. This prevents users from needing to learn the idiosyncrasies of inline files to use the filesystem portably.
-rw-r--r--lfs.c255
-rw-r--r--lfs.h51
2 files changed, 184 insertions, 122 deletions
diff --git a/lfs.c b/lfs.c
index 3a02348..08ba3f0 100644
--- a/lfs.c
+++ b/lfs.c
@@ -366,13 +366,15 @@ static inline bool lfs_gstate_hasmovehere(const struct lfs_gstate *a,
}
static inline void lfs_gstate_fromle32(struct lfs_gstate *a) {
- for (int i = 0; i < 3; i++) {
- ((uint32_t*)a)[i] = lfs_fromle32(((uint32_t*)a)[i]);
- }
+ a->tag = lfs_fromle32(a->tag);
+ a->pair[0] = lfs_fromle32(a->pair[0]);
+ a->pair[1] = lfs_fromle32(a->pair[1]);
}
static inline void lfs_gstate_tole32(struct lfs_gstate *a) {
- lfs_gstate_fromle32(a);
+ a->tag = lfs_tole32(a->tag);
+ a->pair[0] = lfs_tole32(a->pair[0]);
+ a->pair[1] = lfs_tole32(a->pair[1]);
}
// other endianness operations
@@ -391,9 +393,8 @@ static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) {
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->inline_max = lfs_fromle32(superblock->inline_max);
- superblock->attr_max = lfs_fromle32(superblock->attr_max);
superblock->file_max = lfs_fromle32(superblock->file_max);
+ superblock->attr_max = lfs_fromle32(superblock->attr_max);
}
static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
@@ -401,9 +402,8 @@ static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
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->inline_max = lfs_tole32(superblock->inline_max);
- superblock->attr_max = lfs_tole32(superblock->attr_max);
superblock->file_max = lfs_tole32(superblock->file_max);
+ superblock->attr_max = lfs_tole32(superblock->attr_max);
}
@@ -413,19 +413,20 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
static int lfs_dir_compact(lfs_t *lfs,
lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
lfs_mdir_t *source, uint16_t begin, uint16_t end);
+static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file);
+static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file);
+static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans);
+static void lfs_fs_prepmove(lfs_t *lfs,
+ uint16_t id, const lfs_block_t pair[2]);
static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2],
lfs_mdir_t *pdir);
static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2],
lfs_mdir_t *parent);
static int lfs_fs_relocate(lfs_t *lfs,
const lfs_block_t oldpair[2], lfs_block_t newpair[2]);
-static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans);
-static void lfs_fs_prepmove(lfs_t *lfs,
- uint16_t id, const lfs_block_t pair[2]);
static int lfs_fs_forceconsistency(lfs_t *lfs);
static int lfs_deinit(lfs_t *lfs);
-
/// Block allocator ///
static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
lfs_t *lfs = (lfs_t*)p;
@@ -490,8 +491,9 @@ static void lfs_alloc_ack(lfs_t *lfs) {
/// Metadata pair and directory operations ///
-static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
- lfs_tag_t gmask, lfs_tag_t gtag, void *buffer) {
+static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir,
+ lfs_tag_t gmask, lfs_tag_t gtag,
+ lfs_off_t goff, void *gbuffer, lfs_size_t gsize) {
lfs_off_t off = dir->off;
lfs_tag_t ntag = dir->etag;
lfs_stag_t gdiff = 0;
@@ -507,7 +509,7 @@ static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
off -= lfs_tag_dsize(ntag);
lfs_tag_t tag = ntag;
int err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, sizeof(ntag),
+ NULL, &lfs->rcache, sizeof(ntag),
dir->pair[0], off, &ntag, sizeof(ntag));
if (err) {
return err;
@@ -533,16 +535,15 @@ static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
return LFS_ERR_NOENT;
}
- lfs_size_t diff = lfs_min(lfs_tag_size(tag),
- lfs_tag_size(gtag));
+ lfs_size_t diff = lfs_min(lfs_tag_size(tag), gsize);
err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, diff,
- dir->pair[0], off+sizeof(tag), buffer, diff);
+ NULL, &lfs->rcache, diff,
+ dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff);
if (err) {
return err;
}
- memset((uint8_t*)buffer + diff, 0,
+ memset((uint8_t*)gbuffer + diff, 0,
lfs_tag_size(gtag) - diff);
return tag + gdiff;
@@ -552,6 +553,74 @@ static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
return LFS_ERR_NOENT;
}
+static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
+ lfs_tag_t gmask, lfs_tag_t gtag, void *buffer) {
+ return lfs_dir_getslice(lfs, dir,
+ gmask, gtag,
+ 0, buffer, lfs_tag_size(gtag));
+}
+
+static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir,
+ const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,
+ 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) {
+ return LFS_ERR_CORRUPT;
+ }
+
+ while (size > 0) {
+ lfs_size_t diff = size;
+
+ if (pcache && pcache->block == 0xfffffffe &&
+ off < pcache->off + pcache->size) {
+ if (off >= pcache->off) {
+ // is already in pcache?
+ diff = lfs_min(diff, pcache->size - (off-pcache->off));
+ memcpy(data, &pcache->buffer[off-pcache->off], diff);
+
+ data += diff;
+ off += diff;
+ size -= diff;
+ continue;
+ }
+
+ // pcache takes priority
+ diff = lfs_min(diff, pcache->off-off);
+ }
+
+ if (rcache->block == 0xfffffffe &&
+ off < rcache->off + rcache->size) {
+ if (off >= rcache->off) {
+ // is already in rcache?
+ diff = lfs_min(diff, rcache->size - (off-rcache->off));
+ memcpy(data, &rcache->buffer[off-rcache->off], diff);
+
+ data += diff;
+ off += diff;
+ size -= diff;
+ continue;
+ }
+
+ // rcache takes priority
+ diff = lfs_min(diff, rcache->off-off);
+ }
+
+ // load to cache, first condition can no longer fail
+ rcache->block = 0xfffffffe;
+ rcache->off = lfs_aligndown(off, lfs->cfg->read_size);
+ rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size),
+ lfs->cfg->cache_size);
+ int err = lfs_dir_getslice(lfs, dir, gmask, gtag,
+ rcache->off, rcache->buffer, rcache->size);
+ if (err) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static int lfs_dir_traverse_filter(void *p,
lfs_tag_t tag, const void *buffer) {
lfs_tag_t *filtertag = p;
@@ -588,7 +657,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
if (off+lfs_tag_dsize(ptag) < dir->off) {
off += lfs_tag_dsize(ptag);
int err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, sizeof(tag),
+ NULL, &lfs->rcache, sizeof(tag),
dir->pair[0], off, &tag, sizeof(tag));
if (err) {
return err;
@@ -688,7 +757,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
int r = 0;
for (int i = 0; i < 2; i++) {
int err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, sizeof(revs[i]),
+ NULL, &lfs->rcache, sizeof(revs[i]),
pair[i], 0, &revs[i], sizeof(revs[i]));
revs[i] = lfs_fromle32(revs[i]);
if (err && err != LFS_ERR_CORRUPT) {
@@ -724,7 +793,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
lfs_tag_t tag;
off += lfs_tag_dsize(ptag);
int err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
+ NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], off, &tag, sizeof(tag));
if (err) {
if (err == LFS_ERR_CORRUPT) {
@@ -751,7 +820,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// check the crc attr
uint32_t dcrc;
err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
+ NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc));
if (err) {
if (err == LFS_ERR_CORRUPT) {
@@ -825,7 +894,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
tempsplit = (lfs_tag_chunk(tag) & 1);
err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
+ NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], off+sizeof(tag), &temptail, 8);
if (err) {
if (err == LFS_ERR_CORRUPT) {
@@ -1137,7 +1206,7 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
// rely on caching to make this efficient
uint8_t dat;
err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, dsize-sizeof(tag)-i,
+ NULL, &lfs->rcache, dsize-sizeof(tag)-i,
disk->block, disk->off+i, &dat, 1);
if (err) {
return err;
@@ -1162,7 +1231,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
// read erased state from next program unit
lfs_tag_t tag;
int err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, sizeof(tag),
+ NULL, &lfs->rcache, sizeof(tag),
commit->block, off, &tag, sizeof(tag));
if (err && err != LFS_ERR_CORRUPT) {
return err;
@@ -1232,7 +1301,7 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
// rather than clobbering one of the blocks we just pretend
// the revision may be valid
int err = lfs_bd_read(lfs,
- &lfs->pcache, &lfs->rcache, sizeof(dir->rev),
+ NULL, &lfs->rcache, sizeof(dir->rev),
dir->pair[0], 0, &dir->rev, sizeof(dir->rev));
if (err) {
return err;
@@ -1556,6 +1625,27 @@ relocate:
static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
const struct lfs_mattr *attrs, int attrcount) {
+ // check for any inline files that aren't RAM backed and
+ // forcefully evict them, needed for filesystem consistency
+ 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->cache_size) {
+ f->flags &= ~LFS_F_READING;
+ f->off = 0;
+
+ int err = lfs_file_relocate(lfs, f);
+ if (err) {
+ return err;
+ }
+
+ err = lfs_file_flush(lfs, f);
+ if (err) {
+ return err;
+ }
+ }
+ }
+
// calculate changes to the directory
lfs_tag_t deletetag = 0xffffffff;
lfs_tag_t createtag = 0xffffffff;
@@ -2262,7 +2352,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
if (file->ctz.size > 0) {
lfs_stag_t res = lfs_dir_get(lfs, &file->m,
LFS_MKTAG(0x700, 0x3ff, 0),
- LFS_MKTAG(LFS_TYPE_STRUCT, file->id, file->ctz.size),
+ LFS_MKTAG(LFS_TYPE_STRUCT, file->id, file->cache.size),
file->cache.buffer);
if (res < 0) {
err = res;
@@ -2306,6 +2396,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
}
static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
+ lfs_alloc_ack(lfs);
while (true) {
// just relocate what exists into new block
lfs_block_t nblock;
@@ -2325,11 +2416,23 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
// either read from dirty cache or disk
for (lfs_off_t i = 0; i < file->off; i++) {
uint8_t data;
- err = lfs_bd_read(lfs,
- &file->cache, &lfs->rcache, file->off-i,
- file->block, i, &data, 1);
- if (err) {
- return err;
+ if (file->flags & LFS_F_INLINE) {
+ err = lfs_dir_getread(lfs, &file->m,
+ // note we evict inline files before they can be dirty
+ NULL, &file->cache, file->off-i,
+ LFS_MKTAG(0xfff, 0x1ff, 0),
+ LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
+ i, &data, 1);
+ if (err) {
+ return err;
+ }
+ } else {
+ err = lfs_bd_read(lfs,
+ &file->cache, &lfs->rcache, file->off-i,
+ file->block, i, &data, 1);
+ if (err) {
+ return err;
+ }
}
err = lfs_bd_prog(lfs,
@@ -2351,6 +2454,8 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
lfs_cache_zero(lfs, &lfs->pcache);
file->block = nblock;
+ file->flags &= ~LFS_F_INLINE;
+ file->flags |= LFS_F_WRITING;
return 0;
relocate:
@@ -2362,9 +2467,7 @@ relocate:
}
static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
- if (file->flags & LFS_F_READING) {
- file->flags &= ~LFS_F_READING;
- }
+ file->flags &= ~LFS_F_READING;
if (file->flags & LFS_F_WRITING) {
lfs_off_t pos = file->pos;
@@ -2403,8 +2506,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
// write out what we have
while (true) {
- int err = lfs_bd_flush(lfs,
- &file->cache, &lfs->rcache, true);
+ int err = lfs_bd_flush(lfs, &file->cache, &lfs->rcache, true);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
@@ -2486,17 +2588,11 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
relocate:
// inline file doesn't fit anymore
- file->block = 0xfffffffe;
file->off = file->pos;
-
- lfs_alloc_ack(lfs);
err = lfs_file_relocate(lfs, file);
if (err) {
return err;
}
-
- file->flags &= ~LFS_F_INLINE;
- file->flags |= LFS_F_WRITING;
}
}
@@ -2546,11 +2642,22 @@ 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 - file->off);
- int err = lfs_bd_read(lfs,
- NULL, &file->cache, lfs->cfg->block_size,
- file->block, file->off, data, diff);
- if (err) {
- return err;
+ if (file->flags & LFS_F_INLINE) {
+ int err = lfs_dir_getread(lfs, &file->m,
+ NULL, &file->cache, lfs->cfg->block_size,
+ LFS_MKTAG(0xfff, 0x1ff, 0),
+ LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
+ file->off, data, diff);
+ if (err) {
+ return err;
+ }
+ } else {
+ int err = lfs_bd_read(lfs,
+ NULL, &file->cache, lfs->cfg->block_size,
+ file->block, file->off, data, diff);
+ if (err) {
+ return err;
+ }
}
file->pos += diff;
@@ -2602,20 +2709,15 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
}
if ((file->flags & LFS_F_INLINE) &&
- file->pos + nsize > lfs->inline_max) {
+ lfs_max(file->pos+nsize, file->ctz.size) >
+ lfs_min(lfs->cfg->cache_size, LFS_ATTR_MAX)) {
// inline file doesn't fit anymore
- file->block = 0xfffffffe;
file->off = file->pos;
-
- lfs_alloc_ack(lfs);
int err = lfs_file_relocate(lfs, file);
if (err) {
file->flags |= LFS_F_ERRED;
return err;
}
-
- file->flags &= ~LFS_F_INLINE;
- file->flags |= LFS_F_WRITING;
}
while (nsize > 0) {
@@ -3101,11 +3203,10 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->name_max = LFS_NAME_MAX;
}
- LFS_ASSERT(lfs->cfg->inline_max <= LFS_INLINE_MAX);
- LFS_ASSERT(lfs->cfg->inline_max <= lfs->cfg->cache_size);
- lfs->inline_max = lfs->cfg->inline_max;
- if (!lfs->inline_max) {
- lfs->inline_max = lfs_min(LFS_INLINE_MAX, lfs->cfg->cache_size);
+ LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX);
+ lfs->file_max = lfs->cfg->file_max;
+ if (!lfs->file_max) {
+ lfs->file_max = LFS_FILE_MAX;
}
LFS_ASSERT(lfs->cfg->attr_max <= LFS_ATTR_MAX);
@@ -3114,12 +3215,6 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->attr_max = LFS_ATTR_MAX;
}
- LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX);
- lfs->file_max = lfs->cfg->file_max;
- if (!lfs->file_max) {
- lfs->file_max = LFS_FILE_MAX;
- }
-
// setup default state
lfs->root[0] = 0xffffffff;
lfs->root[1] = 0xffffffff;
@@ -3182,9 +3277,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
.block_size = lfs->cfg->block_size,
.block_count = lfs->cfg->block_count,
.name_max = lfs->name_max,
- .inline_max = lfs->inline_max,
- .attr_max = lfs->attr_max,
.file_max = lfs->file_max,
+ .attr_max = lfs->attr_max,
};
lfs_superblock_tole32(&superblock);
@@ -3270,15 +3364,15 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->name_max = superblock.name_max;
}
- if (superblock.inline_max) {
- if (superblock.inline_max > lfs->inline_max) {
- LFS_ERROR("Unsupported inline_max (%"PRIu32" > %"PRIu32")",
- superblock.inline_max, lfs->inline_max);
+ if (superblock.file_max) {
+ if (superblock.file_max > lfs->file_max) {
+ LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")",
+ superblock.file_max, lfs->file_max);
err = LFS_ERR_INVAL;
goto cleanup;
}
- lfs->inline_max = superblock.inline_max;
+ lfs->file_max = superblock.file_max;
}
if (superblock.attr_max) {
@@ -3291,17 +3385,6 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->attr_max = superblock.attr_max;
}
-
- if (superblock.file_max) {
- if (superblock.file_max > lfs->file_max) {
- LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")",
- superblock.file_max, lfs->file_max);
- err = LFS_ERR_INVAL;
- goto cleanup;
- }
-
- lfs->file_max = superblock.file_max;
- }
}
// has gstate?
diff --git a/lfs.h b/lfs.h
index 61ef66f..ec41c2f 100644
--- a/lfs.h
+++ b/lfs.h
@@ -51,30 +51,21 @@ typedef uint32_t lfs_block_t;
#define LFS_NAME_MAX 255
#endif
-// Maximum inline file size in bytes, may be redefined to limit RAM usage,
-// but littlefs will automatically limit the LFS_INLINE_MAX to the
-// configured cache_size. Limited to <= 1022. Stored in superblock and must
-// be respected by other littlefs drivers.
-#ifndef LFS_INLINE_MAX
-#define LFS_INLINE_MAX 1022
-#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. Stored
-// in superblock and must be respected by other littlefs drivers.
-#ifndef LFS_ATTR_MAX
-#define LFS_ATTR_MAX 1022
-#endif
-
// Maximum size of a file in bytes, may be redefined to limit to support other
// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return
-// incorrect values due to signed sizes. Stored in superblock and must be
-// respected by other littlefs drivers.
+// incorrect values due to using signed integers. Stored in superblock and
+// must be respected by other littlefs drivers.
#ifndef LFS_FILE_MAX
#define LFS_FILE_MAX 2147483647
#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
+#define LFS_ATTR_MAX 1022
+#endif
+
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
@@ -234,24 +225,15 @@ struct lfs_config {
// superblock and must be respected by other littlefs drivers.
lfs_size_t name_max;
- // Optional upper limit on inlined files in bytes. Inline files must be
- // backed by RAM, but if a file fits in RAM it can be inlined into its
- // directory block without needing its own data block. Must be <=
- // cache_size and LFS_INLINE_MAX. Defaults to min(LFS_INLINE_MAX,
- // cache_size) when zero. Stored in superblock and must be respected by
- // other littlefs drivers.
- lfs_size_t inline_max;
-
- // 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. Stored in superblock and must be respected by
- // other littlefs drivers.
- lfs_size_t attr_max;
-
// Optional upper limit on files in bytes. No downside for larger files
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
// in superblock and must be respected by other littlefs drivers.
lfs_size_t file_max;
+
+ // 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;
};
// File info structure
@@ -362,11 +344,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 inline_max;
- lfs_size_t attr_max;
lfs_size_t file_max;
+ lfs_size_t attr_max;
} lfs_superblock_t;
// The littlefs filesystem type
@@ -398,9 +378,8 @@ typedef struct lfs {
const struct lfs_config *cfg;
lfs_size_t name_max;
- lfs_size_t inline_max;
- lfs_size_t attr_max;
lfs_size_t file_max;
+ lfs_size_t attr_max;
} lfs_t;