diff options
author | Christopher Haster <chaster@utexas.edu> | 2018-09-11 06:07:59 +0300 |
---|---|---|
committer | Christopher Haster <chaster@utexas.edu> | 2018-10-18 18:00:49 +0300 |
commit | 5eeeb9d6ac0e71f182c107a540939a56b26bb957 (patch) | |
tree | c137bd135d09b997c7988b20e906a4cf9103432d | |
parent | 617dd87621e7e5120427fd0887dbfcdc8af525ac (diff) |
Revisited some generic concepts, callbacks, and some reorganization
- Callbacks for get/match, this does have a code cost, but allows more
code reuse, which almost balances out the code cost, but also reduces
maintenance and increased flexibility. Also callbacks may be able to
be gc-ed in some cases.
- Consistent struct vs _t usage, _t for external-facing struct that
shouldn't be messed with outside the library. structs for external and
internal structs where anyone with access is allowed to modify.
- Reorganized several high-level function groups
- Inlined structures that didn't need separate definitions in header
-rw-r--r-- | lfs.c | 1184 | ||||
-rw-r--r-- | lfs.h | 44 |
2 files changed, 627 insertions, 601 deletions
@@ -224,83 +224,8 @@ static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { } -/// Internal operations predeclared here /// -static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], - lfs_mdir_t *pdir); -static int32_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 int lfs_fs_deorphan(lfs_t *lfs); -static int lfs_fs_demove(lfs_t *lfs); -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; - lfs_block_t off = ((block - lfs->free.off) - + lfs->cfg->block_count) % lfs->cfg->block_count; - - if (off < lfs->free.size) { - lfs->free.buffer[off / 32] |= 1U << (off % 32); - } - - return 0; -} - -static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { - while (true) { - while (lfs->free.i != lfs->free.size) { - lfs_block_t off = lfs->free.i; - lfs->free.i += 1; - lfs->free.ack -= 1; - - if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { - // found a free block - *block = (lfs->free.off + off) % lfs->cfg->block_count; - - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs->free.i != lfs->free.size && - (lfs->free.buffer[lfs->free.i / 32] - & (1U << (lfs->free.i % 32)))) { - lfs->free.i += 1; - lfs->free.ack -= 1; - } - - return 0; - } - } - - // check if we have looked at all blocks since last ack - if (lfs->free.ack == 0) { - LFS_WARN("No more free space %"PRIu32, - lfs->free.i + lfs->free.off); - return LFS_ERR_NOSPC; - } - - lfs->free.off = (lfs->free.off + lfs->free.size) - % lfs->cfg->block_count; - lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->free.ack); - lfs->free.i = 0; - - // find mask of free blocks from tree - memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8); - int err = lfs_fs_traverse(lfs, lfs_alloc_lookahead, lfs); - if (err) { - return err; - } - } -} - -static void lfs_alloc_ack(lfs_t *lfs) { - lfs->free.ack = lfs->cfg->block_count; -} - - -/// Metadata pair and directory operations /// +/// Small type-level utilities /// +// operations on block pairs static inline void lfs_pair_swap(lfs_block_t pair[2]) { lfs_block_t t = pair[0]; pair[0] = pair[1]; @@ -335,75 +260,60 @@ static inline void lfs_pair_tole32(lfs_block_t pair[2]) { pair[1] = lfs_tole32(pair[1]); } -static void lfs_ctz_fromle32(struct lfs_ctz *ctz) { - ctz->head = lfs_fromle32(ctz->head); - ctz->size = lfs_fromle32(ctz->size); -} - -static void lfs_ctz_tole32(struct lfs_ctz *ctz) { - ctz->head = lfs_tole32(ctz->head); - ctz->size = lfs_tole32(ctz->size); -} +// operations on 32-bit entry tags +typedef uint32_t lfs_tag_t; +typedef int32_t lfs_stag_t; -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->inline_max = lfs_fromle32(superblock->inline_max); - superblock->attr_max = lfs_fromle32(superblock->attr_max); - superblock->name_max = lfs_fromle32(superblock->name_max); -} - -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->inline_max = lfs_tole32(superblock->inline_max); - superblock->attr_max = lfs_tole32(superblock->attr_max); - superblock->name_max = lfs_tole32(superblock->name_max); -} - - - -/// Entry tag operations /// #define LFS_MKTAG(type, id, size) \ - (((uint32_t)(type) << 22) | ((uint32_t)(id) << 12) | (uint32_t)(size)) - -#define LFS_MKATTR(type, id, buffer, size, next) \ - &(const lfs_mattr_t){LFS_MKTAG(type, id, size), (buffer), (next)} + (((lfs_tag_t)(type) << 22) | ((lfs_tag_t)(id) << 12) | (lfs_tag_t)(size)) -static inline bool lfs_tag_isvalid(uint32_t tag) { +static inline bool lfs_tag_isvalid(lfs_tag_t tag) { return !(tag & 0x80000000); } -static inline bool lfs_tag_isuser(uint32_t tag) { +static inline bool lfs_tag_isuser(lfs_tag_t tag) { return (tag & 0x40000000); } -static inline bool lfs_tag_isdelete(uint32_t tag) { +static inline bool lfs_tag_isdelete(lfs_tag_t tag) { return (tag & 0x00000fff) == 0xfff; } -static inline uint16_t lfs_tag_type(uint32_t tag) { +static inline uint16_t lfs_tag_type(lfs_tag_t tag) { return (tag & 0x7fc00000) >> 22; } -static inline uint16_t lfs_tag_subtype(uint32_t tag) { +static inline uint16_t lfs_tag_subtype(lfs_tag_t tag) { return ((tag & 0x7c000000) >> 26) << 4; } -static inline uint16_t lfs_tag_id(uint32_t tag) { +static inline uint16_t lfs_tag_id(lfs_tag_t tag) { return (tag & 0x003ff000) >> 12; } -static inline lfs_size_t lfs_tag_size(uint32_t tag) { +static inline lfs_size_t lfs_tag_size(lfs_tag_t tag) { return tag & 0x00000fff; } -static inline lfs_size_t lfs_tag_dsize(uint32_t tag) { +static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { return sizeof(tag) + lfs_tag_size(tag + lfs_tag_isdelete(tag)); } +// operations on attributes in attribute lists +struct lfs_mattr { + lfs_tag_t tag; + const void *buffer; + const struct lfs_mattr *next; +}; + +#define LFS_MKATTR(type, id, buffer, size, next) \ + &(const struct lfs_mattr){LFS_MKTAG(type, id, size), (buffer), (next)} + +struct lfs_diskoff { + lfs_block_t block; + lfs_off_t off; +}; + // operations on set of globals static inline void lfs_global_xor(lfs_global_t *a, const lfs_global_t *b) { for (int i = 0; i < sizeof(lfs_global_t)/4; i++) { @@ -453,196 +363,126 @@ static inline void lfs_global_orphans(lfs_t *lfs, int8_t orphans) { lfs->globals.g.orphans += orphans; } +// other endianness operations +static void lfs_ctz_fromle32(struct lfs_ctz *ctz) { + ctz->head = lfs_fromle32(ctz->head); + ctz->size = lfs_fromle32(ctz->size); +} -// commit logic -struct lfs_commit { - lfs_block_t block; - lfs_off_t off; - uint32_t ptag; - uint32_t crc; - - lfs_off_t begin; - lfs_off_t end; - lfs_off_t ack; -}; - -struct lfs_diskoff { - lfs_block_t block; - lfs_off_t off; -}; - -static int32_t lfs_commit_get(lfs_t *lfs, - lfs_block_t block, lfs_off_t off, uint32_t tag, bool compacting, - uint32_t getmask, uint32_t gettag, int32_t getdiff, void *buffer) { - gettag += getdiff; - - // iterate over dir block backwards (for faster lookups) - while (off >= sizeof(uint32_t) + lfs_tag_dsize(tag)) { - off -= lfs_tag_dsize(tag); - - if (lfs_tag_subtype(tag) == LFS_TYPE_CRC && compacting) { - break; - } else if (lfs_tag_subtype(tag) == LFS_TYPE_DELETE) { - // something was deleted, need to move around it - if (lfs_tag_id(tag) <= lfs_tag_id(gettag - getdiff)) { - getdiff -= LFS_MKTAG(0, 1, 0); - } - } - - if ((tag & getmask) == ((gettag - getdiff) & getmask)) { - if (lfs_tag_isdelete(tag) && !compacting) { - return LFS_ERR_NOENT; - } +static void lfs_ctz_tole32(struct lfs_ctz *ctz) { + ctz->head = lfs_tole32(ctz->head); + ctz->size = lfs_tole32(ctz->size); +} - if (buffer) { - lfs_size_t diff = lfs_min( - lfs_tag_size(gettag), lfs_tag_size(tag)); - int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, diff, - block, off+sizeof(tag), buffer, diff); - if (err) { - return err; - } +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->inline_max = lfs_fromle32(superblock->inline_max); + superblock->attr_max = lfs_fromle32(superblock->attr_max); + superblock->name_max = lfs_fromle32(superblock->name_max); +} - memset((uint8_t*)buffer + diff, 0, - lfs_tag_size(gettag) - diff); - } +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->inline_max = lfs_tole32(superblock->inline_max); + superblock->attr_max = lfs_tole32(superblock->attr_max); + superblock->name_max = lfs_tole32(superblock->name_max); +} - return tag + getdiff; - } - if (lfs_tag_subtype(tag) == LFS_TYPE_NAME) { - // found where something was created - if (lfs_tag_id(tag) == lfs_tag_id(gettag - getdiff)) { - break; - } else if (lfs_tag_id(tag) < lfs_tag_id(gettag - getdiff)) { - getdiff += LFS_MKTAG(0, 1, 0); - } - } +/// Internal operations predeclared here /// +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 int lfs_fs_forceconsistency(lfs_t *lfs); +static int lfs_deinit(lfs_t *lfs); - uint32_t ntag; - int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, sizeof(ntag), - block, off, &ntag, sizeof(ntag)); - if (err) { - return err; - } - tag ^= lfs_fromle32(ntag); - tag &= 0x7fffffff; - } - return LFS_ERR_NOENT; -} +/// Block allocator /// +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->cfg->block_count; -static int lfs_commit_prog(lfs_t *lfs, struct lfs_commit *commit, - const void *buffer, lfs_size_t size) { - lfs_off_t skip = lfs_min(lfs_max(commit->ack, commit->off) - - commit->off, size); - int err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off + skip, - (const uint8_t*)buffer + skip, size - skip); - if (err) { - return err; + if (off < lfs->free.size) { + lfs->free.buffer[off / 32] |= 1U << (off % 32); } - commit->crc = lfs_crc(commit->crc, buffer, size); - commit->off += size; - commit->ack = lfs_max(commit->off, commit->ack); return 0; } -static int lfs_commit_attrs(lfs_t *lfs, struct lfs_commit *commit, - uint16_t id, const struct lfs_attr *attrs); - -static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, int pass, - uint32_t frommask, uint32_t fromtag, int32_t fromdiff, - const lfs_mdir_t *dir, const lfs_mattr_t *attrs); - -static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit, - uint32_t tag, const void *buffer) { - if (lfs_tag_type(tag) == LFS_FROM_MOVE) { - // special case for moves - return lfs_commit_move(lfs, commit, 1, - 0x003ff000, LFS_MKTAG(0, lfs_tag_size(tag), 0), - LFS_MKTAG(0, lfs_tag_id(tag), 0) - - LFS_MKTAG(0, lfs_tag_size(tag), 0), - buffer, NULL); - } else if (lfs_tag_type(tag) == LFS_FROM_ATTRS) { - // special case for custom attributes - return lfs_commit_attrs(lfs, commit, - lfs_tag_id(tag), buffer); - } +static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { + while (true) { + while (lfs->free.i != lfs->free.size) { + lfs_block_t off = lfs->free.i; + lfs->free.i += 1; + lfs->free.ack -= 1; - // check if we fit - lfs_size_t dsize = lfs_tag_dsize(tag); - if (commit->off + dsize > commit->end) { - return LFS_ERR_NOSPC; - } + if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { + // found a free block + *block = (lfs->free.off + off) % lfs->cfg->block_count; - // write out tag - uint32_t ntag = lfs_tole32((tag & 0x7fffffff) ^ commit->ptag); - int err = lfs_commit_prog(lfs, commit, &ntag, sizeof(ntag)); - if (err) { - return err; - } + // eagerly find next off so an alloc ack can + // discredit old lookahead blocks + while (lfs->free.i != lfs->free.size && + (lfs->free.buffer[lfs->free.i / 32] + & (1U << (lfs->free.i % 32)))) { + lfs->free.i += 1; + lfs->free.ack -= 1; + } - if (!(tag & 0x80000000)) { - // from memory - err = lfs_commit_prog(lfs, commit, buffer, dsize-sizeof(tag)); - if (err) { - return err; - } - } else { - // from disk - const struct lfs_diskoff *disk = buffer; - for (lfs_off_t i = 0; i < dsize-sizeof(tag); i++) { - // rely on caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, dsize-sizeof(tag)-i, - disk->block, disk->off+i, &dat, 1); - if (err) { - return err; + return 0; } + } - err = lfs_commit_prog(lfs, commit, &dat, 1); - if (err) { - return err; - } + // check if we have looked at all blocks since last ack + if (lfs->free.ack == 0) { + LFS_WARN("No more free space %"PRIu32, + lfs->free.i + lfs->free.off); + return LFS_ERR_NOSPC; } - } - commit->ptag = tag & 0x7fffffff; - return 0; -} + lfs->free.off = (lfs->free.off + lfs->free.size) + % lfs->cfg->block_count; + lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->free.ack); + lfs->free.i = 0; -static int lfs_commit_attrs(lfs_t *lfs, struct lfs_commit *commit, - uint16_t id, const struct lfs_attr *attrs) { - for (const struct lfs_attr *a = attrs; a; a = a->next) { - int err = lfs_commit_attr(lfs, commit, - LFS_MKTAG(0x100 | a->type, id, a->size), a->buffer); + // find mask of free blocks from tree + memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8); + int err = lfs_fs_traverse(lfs, lfs_alloc_lookahead, lfs); if (err) { return err; } } +} - return 0; +static void lfs_alloc_ack(lfs_t *lfs) { + lfs->free.ack = lfs->cfg->block_count; } -static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, int pass, - uint32_t frommask, uint32_t fromtag, int32_t fromdiff, - const lfs_mdir_t *dir, const lfs_mattr_t *attrs) { - fromtag += fromdiff; - // iterate through list and commits, only committing unique entries +/// Metadata pair and directory operations /// +static int lfs_dir_traverse(lfs_t *lfs, + const lfs_mdir_t *dir, const struct lfs_mattr *attrs, + lfs_tag_t matchmask, lfs_tag_t matchtag, lfs_stag_t matchdiff, + int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { + lfs_block_t block = dir->pair[0]; lfs_off_t off = dir->off; - uint32_t ntag = dir->etag; - bool end = false; - while (attrs || off >= sizeof(uint32_t) + lfs_tag_dsize(ntag)) { - struct lfs_diskoff disk; - uint32_t tag; + lfs_tag_t ntag = dir->etag; + bool lastcommit = false; + matchtag += matchdiff; + + // iterate over dir block backwards (for faster lookups) + while (attrs || off >= sizeof(lfs_tag_t) + lfs_tag_dsize(ntag)) { + lfs_tag_t tag; const void *buffer; + struct lfs_diskoff disk; if (attrs) { tag = attrs->tag; buffer = attrs->buffer; @@ -652,12 +492,12 @@ static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, int pass, tag = ntag; buffer = &disk; - disk.block = dir->pair[0]; + disk.block = block; disk.off = off + sizeof(tag); int err = lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, sizeof(ntag), - dir->pair[0], off, &ntag, sizeof(ntag)); + block, off, &ntag, sizeof(ntag)); if (err) { return err; } @@ -667,48 +507,28 @@ static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, int pass, } if (lfs_tag_subtype(tag) == LFS_TYPE_CRC) { - end = 2 & lfs_tag_type(tag); - } else if (lfs_tag_subtype(tag) == LFS_TYPE_DELETE && - lfs_tag_id(tag) <= lfs_tag_id(fromtag - fromdiff)) { - // something was deleted, we need to move around it - fromdiff -= LFS_MKTAG(0, 1, 0); - } - - if ((tag & frommask) == ((fromtag - fromdiff) & frommask) && - !(lfs_tag_isdelete(tag) && end)) { - bool duplicate; - if (pass == 0) { - duplicate = (lfs_tag_subtype(tag) != LFS_TYPE_NAME); - } else { - // check if type has already been committed - int32_t res = lfs_commit_get(lfs, - commit->block, commit->off, commit->ptag, true, - lfs_tag_isuser(tag) ? 0x7ffff000 : 0x7c3ff000, - tag + fromdiff, 0, NULL); - if (res < 0 && res != LFS_ERR_NOENT) { - return res; - } - - duplicate = (res != LFS_ERR_NOENT); + lastcommit = 2 & lfs_tag_type(tag); + } else if (lfs_tag_subtype(tag) == LFS_TYPE_DELETE) { + // something was deleted, need to move around it + if (lfs_tag_id(tag) <= lfs_tag_id(matchtag - matchdiff)) { + matchdiff -= LFS_MKTAG(0, 1, 0); } + } - if (!duplicate) { - // update id and commit, as we are currently unique - int err = lfs_commit_attr(lfs, commit, - tag + fromdiff, - buffer); - if (err) { - return err; - } + if ((tag & matchmask) == ((matchtag - matchdiff) & matchmask) && + !(lfs_tag_isdelete(tag) && lastcommit)) { + int res = cb(data, tag + matchdiff, buffer); + if (res) { + return res; } } if (lfs_tag_subtype(tag) == LFS_TYPE_NAME) { // found where something was created - if (lfs_tag_id(tag) == lfs_tag_id(fromtag - fromdiff)) { + if (lfs_tag_id(tag) == lfs_tag_id(matchtag - matchdiff)) { break; - } else if (lfs_tag_id(tag) < lfs_tag_id(fromtag - fromdiff)) { - fromdiff += LFS_MKTAG(0, 1, 0); + } else if (lfs_tag_id(tag) < lfs_tag_id(matchtag - matchdiff)) { + matchdiff += LFS_MKTAG(0, 1, 0); } } } @@ -716,177 +536,63 @@ static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, int pass, return 0; } -static int lfs_commit_globals(lfs_t *lfs, struct lfs_commit *commit, - lfs_global_t *locals) { - if (lfs_global_iszero(&lfs->locals)) { - return 0; - } - - lfs_global_xor(locals, &lfs->locals); - int err = lfs_commit_attr(lfs, commit, - LFS_MKTAG(LFS_TYPE_GLOBALS + locals->l.deorphaned, 0x3ff, 10), - locals); - lfs_global_xor(locals, &lfs->locals); - return err; -} - -static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit, - bool compacting) { - // align to program units - lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t), - lfs->cfg->prog_size); - - // read erased state from next program unit - uint32_t tag; - int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, sizeof(tag), - commit->block, off, &tag, sizeof(tag)); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // build crc tag - bool reset = ~lfs_fromle32(tag) >> 31; - tag = LFS_MKTAG(LFS_TYPE_CRC + (compacting << 1) + reset, - 0x3ff, off - (commit->off+sizeof(uint32_t))); - - // write out crc - uint32_t footer[2]; - footer[0] = lfs_tole32(tag ^ commit->ptag); - commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0])); - footer[1] = lfs_tole32(commit->crc); - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off, &footer, sizeof(footer)); - if (err) { - return err; - } - commit->off += sizeof(tag)+lfs_tag_size(tag); - commit->ptag = tag ^ (reset << 31); - - // flush buffers - err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); - if (err) { - return err; - } - - // successful commit, check checksum to make sure - uint32_t crc = 0xffffffff; - lfs_size_t size = commit->off - lfs_tag_size(tag) - commit->begin; - for (lfs_off_t i = 0; i < size; i++) { - // leave it up to caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, size-i, - commit->block, commit->begin+i, &dat, 1); - if (err) { - return err; - } - - crc = lfs_crc(crc, &dat, 1); - } - - if (err) { - return err; - } - - if (crc != commit->crc) { - return LFS_ERR_CORRUPT; - } - - return 0; -} - -// internal dir operations -static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { - // allocate pair of dir blocks (backwards, so we write block 1 first) - for (int i = 0; i < 2; i++) { - int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); - if (err) { - return err; - } - } - - // 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), - dir->pair[0], 0, &dir->rev, sizeof(dir->rev)); - if (err) { - return err; - } - - dir->rev = lfs_fromle32(dir->rev); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // set defaults - dir->off = sizeof(dir->rev); - dir->etag = 0xffffffff; - dir->count = 0; - dir->tail[0] = 0xffffffff; - dir->tail[1] = 0xffffffff; - dir->erased = false; - dir->split = false; - lfs_global_zero(&dir->locals); - - // don't write out yet, let caller take care of that - return 0; -} - -static int32_t lfs_dir_fetchmatch(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_block_t pair[2], - uint32_t findmask, uint32_t findtag, const void *findbuffer) { - dir->pair[0] = pair[0]; - dir->pair[1] = pair[1]; - int32_t foundtag = LFS_ERR_NOENT; - +static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, + lfs_mdir_t *dir, const lfs_block_t pair[2], + lfs_tag_t matchmask, lfs_tag_t matchtag, + int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { // find the block with the most recent revision - uint32_t rev[2]; + uint32_t revs[2]; + int r = 0; for (int i = 0; i < 2; i++) { int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, sizeof(rev[i]), - dir->pair[i], 0, &rev[i], sizeof(rev[i])); - rev[i] = lfs_fromle32(rev[i]); + &lfs->pcache, &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) { return err; } - if (err == LFS_ERR_CORRUPT) { - rev[i] = rev[(i+1)%2] - 1; + if (lfs_scmp(revs[i], revs[(i+1)%2]) > 0 || err == LFS_ERR_CORRUPT) { + r = i; } } - if (lfs_scmp(rev[1], rev[0]) > 0) { + // now fetch the actual dir (and find match) + lfs_stag_t foundtag = LFS_ERR_NOENT; + dir->pair[0] = pair[0]; + dir->pair[1] = pair[1]; + dir->off = 0; + if (r != 0) { lfs_pair_swap(dir->pair); - lfs_pair_swap(rev); + lfs_pair_swap(revs); } - // load blocks and check crc + // scan tags and check crcs for (int i = 0; i < 2; i++) { - lfs_off_t off = sizeof(dir->rev); - uint32_t ptag = 0xffffffff; - uint32_t crc = 0xffffffff; - - dir->rev = lfs_tole32(rev[0]); - crc = lfs_crc(crc, &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - dir->off = 0; - - uint32_t tempfoundtag = foundtag; - uint16_t tempcount = 0; - lfs_block_t temptail[2] = {0xffffffff, 0xffffffff}; - bool tempsplit = false; - lfs_global_t templocals; - lfs_global_zero(&templocals); + lfs_block_t block = dir->pair[0]; + lfs_off_t off = sizeof(uint32_t); + lfs_tag_t ptag = 0xffffffff; + + lfs_tag_t tempfoundtag = foundtag; + lfs_mdir_t temp = { + .pair = {dir->pair[0], dir->pair[1]}, + .rev = revs[0], + .tail = {0xffffffff, 0xffffffff}, + .split = false, + .count = 0, + }; + lfs_global_zero(&temp.locals); + + temp.rev = lfs_tole32(temp.rev); + uint32_t crc = lfs_crc(0xffffffff, &temp.rev, sizeof(temp.rev)); + temp.rev = lfs_fromle32(temp.rev); while (true) { // extract next tag - uint32_t tag; + lfs_tag_t tag; int err = lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off, &tag, sizeof(tag)); + block, off, &tag, sizeof(tag)); if (err) { if (err == LFS_ERR_CORRUPT) { // can't continue? @@ -916,7 +622,7 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, uint32_t dcrc; err = lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); + block, off+sizeof(tag), &dcrc, sizeof(dcrc)); if (err) { if (err == LFS_ERR_CORRUPT) { dir->erased = false; @@ -933,26 +639,21 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, // reset the next bit if we need to tag ^= (lfs_tag_type(tag) & 1) << 31; + lfs->seed ^= crc; + crc = 0xffffffff; // update with what's found so far foundtag = tempfoundtag; + *dir = temp; dir->off = off + lfs_tag_dsize(tag); dir->etag = tag; - dir->count = tempcount; - dir->tail[0] = temptail[0]; - dir->tail[1] = temptail[1]; - dir->split = tempsplit; - dir->locals = templocals; - - lfs->seed ^= crc; - crc = 0xffffffff; } else { // crc the entry first, leaving it in the cache for (lfs_off_t j = sizeof(tag); j < lfs_tag_dsize(tag); j++) { uint8_t dat; err = lfs_bd_read(lfs, NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+j, &dat, 1); + block, off+j, &dat, 1); if (err) { if (err == LFS_ERR_CORRUPT) { dir->erased = false; @@ -966,15 +667,15 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, // check for special tags if (lfs_tag_subtype(tag) == LFS_TYPE_NAME) { - tempcount += 1; + temp.count += 1; if (lfs_tag_isvalid(tempfoundtag) && lfs_tag_id(tag) <= lfs_tag_id(tempfoundtag)) { tempfoundtag += LFS_MKTAG(0, 1, 0); } } else if (lfs_tag_subtype(tag) == LFS_TYPE_DELETE) { - LFS_ASSERT(tempcount > 0); - tempcount -= 1; + LFS_ASSERT(temp.count > 0); + temp.count -= 1; if (lfs_tag_id(tag) == lfs_tag_id(tempfoundtag)) { tempfoundtag = LFS_ERR_NOENT; @@ -983,24 +684,24 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, tempfoundtag -= LFS_MKTAG(0, 1, 0); } } else if (lfs_tag_subtype(tag) == LFS_TYPE_TAIL) { - tempsplit = (lfs_tag_type(tag) & 1); + temp.split = (lfs_tag_type(tag) & 1); err = lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), - &temptail, sizeof(temptail)); + block, off+sizeof(tag), + &temp.tail, sizeof(temp.tail)); if (err) { if (err == LFS_ERR_CORRUPT) { dir->erased = false; break; } } - lfs_pair_fromle32(temptail); + lfs_pair_fromle32(temp.tail); } else if (lfs_tag_subtype(tag) == LFS_TYPE_GLOBALS) { - templocals.l.deorphaned = (lfs_tag_type(tag) & 1); + temp.locals.l.deorphaned = (lfs_tag_type(tag) & 1); err = lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), - &templocals, 10); + block, off+sizeof(tag), + &temp.locals, 10); if (err) { if (err == LFS_ERR_CORRUPT) { dir->erased = false; @@ -1009,35 +710,13 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, } } - if ((tag & findmask) == (findtag & findmask)) { + if ((tag & matchmask) == (matchtag & matchmask)) { // found a match? - if (lfs_tag_isdelete(findtag)) { + if (lfs_tag_isdelete(tag)) { tempfoundtag = LFS_ERR_NOENT; - } else if (lfs_tag_type(findtag) == LFS_TYPE_DIRSTRUCT) { - lfs_block_t child[2]; - err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, - lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), - &child, sizeof(child)); - if (err < 0) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } - - lfs_pair_fromle32(child); - if (lfs_pair_cmp(child, - (const lfs_block_t *)findbuffer) == 0) { - tempfoundtag = tag; - } - } else if (lfs_tag_type(findtag) == LFS_TYPE_NAME) { - int res = lfs_bd_cmp(lfs, - NULL, &lfs->rcache, lfs_tag_size(findtag), - dir->pair[0], off+sizeof(tag), - findbuffer, lfs_tag_size(findtag)); + } else if (cb) { + int res = cb(data, tag, &(struct lfs_diskoff){ + block, off+sizeof(tag)}); if (res < 0) { if (res == LFS_ERR_CORRUPT) { dir->erased = false; @@ -1049,8 +728,6 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, if (res) { tempfoundtag = tag; } - } else { - tempfoundtag = tag; } } } @@ -1076,7 +753,7 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, // failed, try the other crc? lfs_pair_swap(dir->pair); - lfs_pair_swap(rev); + lfs_pair_swap(revs); } LFS_ERROR("Corrupted dir pair at %"PRIu32" %"PRIu32, @@ -1086,8 +763,7 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, static int lfs_dir_fetch(lfs_t *lfs, lfs_mdir_t *dir, const lfs_block_t pair[2]) { - int32_t res = lfs_dir_fetchmatch(lfs, dir, pair, - 0xffffffff, 0xffffffff, NULL); + lfs_stag_t res = lfs_dir_fetchmatch(lfs, dir, pair, 0, 0, NULL, NULL); if (res < 0 && res != LFS_ERR_NOENT) { return res; } @@ -1095,15 +771,16 @@ static int lfs_dir_fetch(lfs_t *lfs, return 0; } -static int32_t lfs_dir_find(lfs_t *lfs, +static lfs_stag_t lfs_dir_findmatch(lfs_t *lfs, lfs_mdir_t *dir, const lfs_block_t pair[2], bool fs, - uint32_t findmask, uint32_t findtag, const void *findbuffer) { + lfs_tag_t findmask, lfs_tag_t findtag, + int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { dir->split = true; dir->tail[0] = pair[0]; dir->tail[1] = pair[1]; while ((dir->split || fs) && !lfs_pair_isnull(dir->tail)) { - int32_t tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, - findmask, findtag, findbuffer); + lfs_stag_t tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, + findmask, findtag, cb, data); if (tag != LFS_ERR_NOENT) { return tag; } @@ -1112,9 +789,41 @@ static int32_t lfs_dir_find(lfs_t *lfs, return LFS_ERR_NOENT; } -static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir, - uint32_t getmask, uint32_t gettag, void *buffer) { - int32_t getdiff = 0; +struct lfs_dir_get_match { + lfs_t *lfs; + void *buffer; + lfs_size_t size; + bool compacting; +}; + +static int lfs_dir_get_match(void *data, + lfs_tag_t tag, const void *buffer) { + struct lfs_dir_get_match *get = data; + lfs_t *lfs = get->lfs; + const struct lfs_diskoff *disk = buffer; + + if (lfs_tag_isdelete(tag) && !get->compacting) { + return LFS_ERR_NOENT; + } + + if (get->buffer) { + lfs_size_t diff = lfs_min(lfs_tag_size(tag), get->size); + int err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, diff, + disk->block, disk->off, get->buffer, diff); + if (err) { + return err; + } + + memset((uint8_t*)get->buffer + diff, 0, get->size - diff); + } + + return tag & 0x7fffffff; +} + +static lfs_stag_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir, + lfs_tag_t getmask, lfs_tag_t gettag, void *buffer) { + lfs_stag_t getdiff = 0; if (lfs_pair_cmp(dir->pair, lfs->globals.g.movepair) == 0 && lfs_tag_id(gettag) <= lfs->globals.g.moveid) { // synthetic moves @@ -1122,13 +831,288 @@ static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir, getdiff -= LFS_MKTAG(0, 1, 0); } - return lfs_commit_get(lfs, - dir->pair[0], dir->off, dir->etag, false, - getmask, gettag, getdiff, buffer); + lfs_stag_t res = lfs_dir_traverse(lfs, dir, NULL, + getmask, gettag, getdiff, + lfs_dir_get_match, &(struct lfs_dir_get_match){ + lfs, buffer, lfs_tag_size(gettag)}); + if (res < 0) { + return res; + } + + return res ? res : LFS_ERR_NOENT; +} + +// commit logic +struct lfs_commit { + lfs_block_t block; + lfs_off_t off; + lfs_tag_t ptag; + uint32_t crc; + + lfs_off_t begin; + lfs_off_t end; + lfs_off_t ack; +}; + +static int lfs_commit_prog(lfs_t *lfs, struct lfs_commit *commit, + const void *buffer, lfs_size_t size) { + lfs_off_t skip = lfs_min(lfs_max(commit->ack, commit->off) + - commit->off, size); + int err = lfs_bd_prog(lfs, + &lfs->pcache, &lfs->rcache, false, + commit->block, commit->off + skip, + (const uint8_t*)buffer + skip, size - skip); + if (err) { + return err; + } + + commit->crc = lfs_crc(commit->crc, buffer, size); + commit->off += size; + commit->ack = lfs_max(commit->off, commit->ack); + return 0; +} + +static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit, + lfs_tag_t tag, const void *buffer); + +struct lfs_commit_move_match { + lfs_t *lfs; + struct lfs_commit *commit; + int pass; +}; + +static int lfs_commit_move_match(void *data, + lfs_tag_t tag, const void *buffer) { + struct lfs_commit_move_match *move = data; + lfs_t *lfs = move->lfs; + struct lfs_commit *commit = move->commit; + + if (move->pass == 0) { + if (lfs_tag_subtype(tag) != LFS_TYPE_NAME) { + return 0; + } + } else { + // check if type has already been committed + lfs_stag_t res = lfs_dir_traverse(lfs, &(const lfs_mdir_t){ + .pair[0] = commit->block, + .off = commit->off, + .etag = commit->ptag}, NULL, + lfs_tag_isuser(tag) ? 0x7ffff000 : 0x7c3ff000, tag, 0, + lfs_dir_get_match, &(struct lfs_dir_get_match){ + lfs, NULL, 0, true}); + if (res < 0 && res != LFS_ERR_NOENT) { + return res; + } + + if (res > 0) { + return 0; + } + } + + // update id and commit, as we are currently unique + return lfs_commit_attr(lfs, commit, tag, buffer); +} + +static int lfs_commit_move(lfs_t *lfs, struct lfs_commit *commit, int pass, + lfs_tag_t frommask, lfs_tag_t fromtag, lfs_stag_t fromdiff, + const lfs_mdir_t *dir, const struct lfs_mattr *attrs) { + return lfs_dir_traverse(lfs, dir, attrs, + frommask, fromtag, fromdiff, + lfs_commit_move_match, &(struct lfs_commit_move_match){ + lfs, commit, pass}); +} + +static int lfs_commit_userattrs(lfs_t *lfs, struct lfs_commit *commit, + uint16_t id, const struct lfs_attr *attrs) { + for (const struct lfs_attr *a = attrs; a; a = a->next) { + int err = lfs_commit_attr(lfs, commit, + LFS_MKTAG(0x100 | a->type, id, a->size), a->buffer); + if (err) { + return err; + } + } + + return 0; +} + +static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit, + lfs_tag_t tag, const void *buffer) { + if (lfs_tag_type(tag) == LFS_FROM_MOVE) { + // special case for moves + return lfs_commit_move(lfs, commit, 1, + 0x003ff000, LFS_MKTAG(0, lfs_tag_size(tag), 0), + LFS_MKTAG(0, lfs_tag_id(tag), 0) - + LFS_MKTAG(0, lfs_tag_size(tag), 0), + buffer, NULL); + } else if (lfs_tag_type(tag) == LFS_FROM_USERATTRS) { + // special case for custom attributes + return lfs_commit_userattrs(lfs, commit, + lfs_tag_id(tag), buffer); + } + + // check if we fit + lfs_size_t dsize = lfs_tag_dsize(tag); + if (commit->off + dsize > commit->end) { + return LFS_ERR_NOSPC; + } + + // write out tag + lfs_tag_t ntag = lfs_tole32((tag & 0x7fffffff) ^ commit->ptag); + int err = lfs_commit_prog(lfs, commit, &ntag, sizeof(ntag)); + if (err) { + return err; + } + + if (!(tag & 0x80000000)) { + // from memory + err = lfs_commit_prog(lfs, commit, buffer, dsize-sizeof(tag)); + if (err) { + return err; + } + } else { + // from disk + const struct lfs_diskoff *disk = buffer; + for (lfs_off_t i = 0; i < dsize-sizeof(tag); i++) { + // rely on caching to make this efficient + uint8_t dat; + err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, dsize-sizeof(tag)-i, + disk->block, disk->off+i, &dat, 1); + if (err) { + return err; + } + + err = lfs_commit_prog(lfs, commit, &dat, 1); + if (err) { + return err; + } + } + } + + commit->ptag = tag & 0x7fffffff; + return 0; +} + +static int lfs_commit_globals(lfs_t *lfs, struct lfs_commit *commit, + lfs_global_t *locals) { + if (lfs_global_iszero(&lfs->locals)) { + return 0; + } + + lfs_global_xor(locals, &lfs->locals); + int err = lfs_commit_attr(lfs, commit, + LFS_MKTAG(LFS_TYPE_GLOBALS + locals->l.deorphaned, 0x3ff, 10), + locals); + lfs_global_xor(locals, &lfs->locals); + return err; +} + +static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit, + bool compacting) { + // align to program units + lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t), + lfs->cfg->prog_size); + + // read erased state from next program unit + lfs_tag_t tag; + int err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, sizeof(tag), + commit->block, off, &tag, sizeof(tag)); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + + // build crc tag + bool reset = ~lfs_fromle32(tag) >> 31; + tag = LFS_MKTAG(LFS_TYPE_CRC + (compacting << 1) + reset, + 0x3ff, off - (commit->off+sizeof(lfs_tag_t))); + + // write out crc + uint32_t footer[2]; + footer[0] = lfs_tole32(tag ^ commit->ptag); + commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0])); + footer[1] = lfs_tole32(commit->crc); + err = lfs_bd_prog(lfs, + &lfs->pcache, &lfs->rcache, false, + commit->block, commit->off, &footer, sizeof(footer)); + if (err) { + return err; + } + commit->off += sizeof(tag)+lfs_tag_size(tag); + commit->ptag = tag ^ (reset << 31); + + // flush buffers + err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); + if (err) { + return err; + } + + // successful commit, check checksum to make sure + uint32_t crc = 0xffffffff; + lfs_size_t size = commit->off - lfs_tag_size(tag) - commit->begin; + for (lfs_off_t i = 0; i < size; i++) { + // leave it up to caching to make this efficient + uint8_t dat; + err = lfs_bd_read(lfs, + NULL, &lfs->rcache, size-i, + commit->block, commit->begin+i, &dat, 1); + if (err) { + return err; + } + + crc = lfs_crc(crc, &dat, 1); + } + + if (err) { + return err; + } + + if (crc != commit->crc) { + return LFS_ERR_CORRUPT; + } + + return 0; +} + +static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { + // allocate pair of dir blocks (backwards, so we write block 1 first) + for (int i = 0; i < 2; i++) { + int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); + if (err) { + return err; + } + } + + // 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), + dir->pair[0], 0, &dir->rev, sizeof(dir->rev)); + if (err) { + return err; + } + + dir->rev = lfs_fromle32(dir->rev); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + + // set defaults + dir->off = sizeof(dir->rev); + dir->etag = 0xffffffff; + dir->count = 0; + dir->tail[0] = 0xffffffff; + dir->tail[1] = 0xffffffff; + dir->erased = false; + dir->split = false; + lfs_global_zero(&dir->locals); + + // don't write out yet, let caller take care of that + return 0; } static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_mattr_t *attrs, + lfs_mdir_t *dir, const struct lfs_mattr *attrs, lfs_mdir_t *source, uint16_t begin, uint16_t end) { // save some state in case block is bad const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; @@ -1375,8 +1359,8 @@ relocate: } static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const lfs_mattr_t *attrs) { - lfs_mattr_t cancelattr; + const struct lfs_mattr *attrs) { + struct lfs_mattr cancelattr; lfs_global_t canceldiff; lfs_global_zero(&canceldiff); if (lfs_pair_cmp(dir->pair, lfs->globals.g.movepair) == 0) { @@ -1395,9 +1379,9 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, } // calculate new directory size - uint32_t deletetag = 0xffffffff; + lfs_tag_t deletetag = 0xffffffff; int attrcount = 0; - for (const lfs_mattr_t *a = attrs; a; a = a->next) { + for (const struct lfs_mattr *a = attrs; a; a = a->next) { if (lfs_tag_subtype(a->tag) == LFS_TYPE_NAME) { dir->count += 1; } else if (lfs_tag_subtype(a->tag) == LFS_TYPE_DELETE) { @@ -1449,7 +1433,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, // iterate over commits backwards, this lets us "append" commits for (int i = 0; i < attrcount; i++) { - const lfs_mattr_t *a = attrs; + const struct lfs_mattr *a = attrs; for (int j = 0; j < attrcount-i-1; j++) { a = a->next; } @@ -1508,7 +1492,7 @@ compact: // two passes, once for things that aren't us, and one // for things that are - for (lfs_mlist_t *d = lfs->mlist; d; d = d->next) { + for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { if (lfs_pair_cmp(d->m.pair, copy.pair) == 0) { d->m = *dir; if (d->id == lfs_tag_id(deletetag)) { @@ -1535,13 +1519,32 @@ compact: return 0; } -static int32_t lfs_dir_lookup(lfs_t *lfs, lfs_mdir_t *dir, const char **path) { +struct lfs_dir_find_match { + lfs_t *lfs; + const void *name; + lfs_size_t size; +}; + +static int lfs_dir_find_match(void *data, + lfs_tag_t tag, const void *buffer) { + struct lfs_dir_find_match *name = data; + lfs_t *lfs = name->lfs; + const struct lfs_diskoff *disk = buffer; + (void)tag; + + return lfs_bd_cmp(lfs, + NULL, &lfs->rcache, name->size, + disk->block, disk->off, name->name, name->size); +} + +static lfs_stag_t lfs_dir_find(lfs_t *lfs, + lfs_mdir_t *dir, const char **path) { // we reduce path to a single name if we can find it const char *name = *path; *path = NULL; // default to root dir - int32_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); + lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); lfs_block_t pair[2] = {lfs->root[0], lfs->root[1]}; while (true) { @@ -1598,7 +1601,7 @@ nextname: // grab the entry data if (lfs_tag_id(tag) != 0x3ff) { - int32_t res = lfs_dir_get(lfs, dir, 0x7c3ff000, + lfs_stag_t res = lfs_dir_get(lfs, dir, 0x7c3ff000, LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); if (res < 0) { return res; @@ -1607,8 +1610,10 @@ nextname: } // find entry matching name - tag = lfs_dir_find(lfs, dir, pair, false, 0x7c000fff, - LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), name); + tag = lfs_dir_findmatch(lfs, dir, pair, false, 0x7c000fff, + LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), + lfs_dir_find_match, &(struct lfs_dir_find_match){ + lfs, name, namelen}); if (tag < 0) { return tag; } @@ -1627,7 +1632,7 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir, return 0; } - int32_t tag = lfs_dir_get(lfs, dir, 0x7c3ff000, + lfs_stag_t tag = lfs_dir_get(lfs, dir, 0x7c3ff000, LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name); if (tag < 0) { return tag; @@ -1661,7 +1666,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { } lfs_mdir_t cwd; - int32_t res = lfs_dir_lookup(lfs, &cwd, &path); + lfs_stag_t res = lfs_dir_find(lfs, &cwd, &path); if (!(res == LFS_ERR_NOENT && path)) { return (res < 0) ? res : LFS_ERR_EXIST; } @@ -1707,7 +1712,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { } int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { - int32_t tag = lfs_dir_lookup(lfs, &dir->m, &path); + lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path); if (tag < 0) { return tag; } @@ -1723,7 +1728,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { pair[1] = lfs->root[1]; } else { // get dir pair from parent - int32_t res = lfs_dir_get(lfs, &dir->m, 0x7c3ff000, + lfs_stag_t res = lfs_dir_get(lfs, &dir->m, 0x7c3ff000, LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); if (res < 0) { return res; @@ -1746,15 +1751,15 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { // add to list of mdirs dir->type = LFS_TYPE_DIR; dir->next = (lfs_dir_t*)lfs->mlist; - lfs->mlist = (lfs_mlist_t*)dir; + lfs->mlist = (struct lfs_mlist*)dir; return 0; } int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { // remove from list of mdirs - for (lfs_mlist_t **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (lfs_mlist_t*)dir) { + for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { + if (*p == (struct lfs_mlist*)dir) { *p = (*p)->next; break; } @@ -2071,7 +2076,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, file->cache.buffer = NULL; // allocate entry for file if it doesn't exist - int32_t tag = lfs_dir_lookup(lfs, &file->m, &path); + lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path); if (tag < 0 && !(tag == LFS_ERR_NOENT && path)) { err = tag; goto cleanup; @@ -2081,7 +2086,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, file->id = lfs_tag_id(tag); file->type = LFS_TYPE_REG; file->next = (lfs_file_t*)lfs->mlist; - lfs->mlist = (lfs_mlist_t*)file; + lfs->mlist = (struct lfs_mlist*)file; if (tag == LFS_ERR_NOENT) { if (!(flags & LFS_O_CREAT)) { @@ -2132,7 +2137,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, // fetch attrs for (const struct lfs_attr *a = file->cfg->attrs; a; a = a->next) { if ((file->flags & 3) != LFS_O_WRONLY) { - int32_t res = lfs_dir_get(lfs, &file->m, 0x7ffff000, + lfs_stag_t res = lfs_dir_get(lfs, &file->m, 0x7ffff000, LFS_MKTAG(0x100 | a->type, file->id, a->size), a->buffer); if (res < 0 && res != LFS_ERR_NOENT) { err = res; @@ -2175,7 +2180,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, // don't always read (may be new/trunc file) if (file->ctz.size > 0) { - int32_t res = lfs_dir_get(lfs, &file->m, 0x7c3ff000, + lfs_stag_t res = lfs_dir_get(lfs, &file->m, 0x7c3ff000, LFS_MKTAG(LFS_TYPE_STRUCT, file->id, file->ctz.size), file->cache.buffer); if (res < 0) { @@ -2204,8 +2209,8 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { int err = lfs_file_sync(lfs, file); // remove from list of mdirs - for (lfs_mlist_t **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (lfs_mlist_t*)file) { + for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { + if (*p == (struct lfs_mlist*)file) { *p = (*p)->next; break; } @@ -2383,7 +2388,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { // commit file data and attributes err = lfs_dir_commit(lfs, &file->m, - LFS_MKATTR(LFS_FROM_ATTRS, file->id, file->cfg->attrs, 0, + LFS_MKATTR(LFS_FROM_USERATTRS, + file->id, file->cfg->attrs, 0, LFS_MKATTR(type, file->id, buffer, size, NULL))); if (err) { @@ -2704,7 +2710,7 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { /// General fs operations /// int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { lfs_mdir_t cwd; - int32_t tag = lfs_dir_lookup(lfs, &cwd, &path); + lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path); if (tag < 0) { return tag; } @@ -2725,7 +2731,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { return err; } - int32_t tag = lfs_dir_lookup(lfs, &cwd, &path); + lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path); if (tag < 0) { return tag; } @@ -2734,7 +2740,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { if (lfs_tag_type(tag) == LFS_TYPE_DIR) { // must be empty before removal lfs_block_t pair[2]; - int32_t res = lfs_dir_get(lfs, &cwd, 0x7c3ff000, + lfs_stag_t res = lfs_dir_get(lfs, &cwd, 0x7c3ff000, LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); if (res < 0) { return res; @@ -2796,14 +2802,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { // find old entry lfs_mdir_t oldcwd; - int32_t oldtag = lfs_dir_lookup(lfs, &oldcwd, &oldpath); + lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath); if (oldtag < 0) { return oldtag; } // find new entry lfs_mdir_t newcwd; - int32_t prevtag = lfs_dir_lookup(lfs, &newcwd, &newpath); + lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath); if (prevtag < 0 && prevtag != LFS_ERR_NOENT) { return prevtag; } @@ -2825,7 +2831,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { } else if (lfs_tag_type(prevtag) == LFS_TYPE_DIR) { // must be empty before removal lfs_block_t prevpair[2]; - int32_t res = lfs_dir_get(lfs, &newcwd, 0x7c3ff000, + lfs_stag_t res = lfs_dir_get(lfs, &newcwd, 0x7c3ff000, LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair); if (res < 0) { return res; @@ -2897,7 +2903,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, uint8_t type, void *buffer, lfs_size_t size) { lfs_mdir_t cwd; - int32_t res = lfs_dir_lookup(lfs, &cwd, &path); + lfs_stag_t res = lfs_dir_find(lfs, &cwd, &path); if (res < 0) { return res; } @@ -2928,7 +2934,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, static int lfs_commitattr(lfs_t *lfs, const char *path, uint8_t type, const void *buffer, lfs_size_t size) { lfs_mdir_t cwd; - int32_t res = lfs_dir_lookup(lfs, &cwd, &path); + lfs_stag_t res = lfs_dir_find(lfs, &cwd, &path); if (res < 0) { return res; } @@ -2958,7 +2964,7 @@ int lfs_setattr(lfs_t *lfs, const char *path, } int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) { - return lfs_commitattr(lfs, path, type, NULL, 0xfff); + return lfs_commitattr(lfs, path, type, NULL, LFS_ATTR_MAX+1); } @@ -3131,15 +3137,17 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { // find root/superblock lfs_mdir_t root; lfs_superblock_t superblock; - int32_t tag = lfs_dir_find(lfs, + lfs_stag_t tag = lfs_dir_findmatch(lfs, &root, (const lfs_block_t[2]){0, 1}, false, 0x7fc00000, - LFS_MKTAG(LFS_TYPE_ROOT, 0, 8), "littlefs"); + LFS_MKTAG(LFS_TYPE_ROOT, 0, 8), + lfs_dir_find_match, &(struct lfs_dir_find_match){ + lfs, "littlefs", 8}); if (tag < 0) { err = tag; goto cleanup; } - int32_t res = lfs_dir_get(lfs, &root, 0x7f800000, + lfs_stag_t res = lfs_dir_get(lfs, &root, 0x7f800000, LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, sizeof(superblock)), &superblock); if (res < 0) { @@ -3263,7 +3271,7 @@ int lfs_fs_traverse(lfs_t *lfs, for (uint16_t id = 0; id < dir.count; id++) { struct lfs_ctz ctz; - int32_t tag = lfs_dir_get(lfs, &dir, 0x7c3ff000, + lfs_stag_t tag = lfs_dir_get(lfs, &dir, 0x7c3ff000, LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); if (tag < 0) { if (tag == LFS_ERR_NOENT) { @@ -3332,7 +3340,31 @@ static int lfs_fs_pred(lfs_t *lfs, return LFS_ERR_NOENT; } -static int32_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], +struct lfs_fs_parent_match { + lfs_t *lfs; + const lfs_block_t pair[2]; +}; + +static int lfs_fs_parent_match(void *data, + lfs_tag_t tag, const void *buffer) { + struct lfs_fs_parent_match *find = data; + lfs_t *lfs = find->lfs; + const struct lfs_diskoff *disk = buffer; + (void)tag; + + lfs_block_t child[2]; + int err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, + disk->block, disk->off, &child, sizeof(child)); + if (err) { + return err; + } + + lfs_pair_fromle32(child); + return (lfs_pair_cmp(child, find->pair) == 0); +} + +static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], lfs_mdir_t *parent) { if (lfs_pair_isnull(lfs->root)) { return LFS_ERR_NOENT; @@ -3340,9 +3372,11 @@ static int32_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], // search for both orderings so we can reuse the find function for (int i = 0; i < 2; i++) { - int32_t tag = lfs_dir_find(lfs, parent, + struct lfs_fs_parent_match match = {lfs, {pair[0], pair[1]}}; + lfs_stag_t tag = lfs_dir_findmatch(lfs, parent, (const lfs_block_t[2]){0, 1}, true, 0x7fc00fff, - LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), pair); + LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), + lfs_fs_parent_match, &match); if (tag != LFS_ERR_NOENT) { return tag; } @@ -3362,7 +3396,7 @@ static int lfs_fs_relocate(lfs_t *lfs, } // update internally tracked dirs - for (lfs_mlist_t *d = lfs->mlist; d; d = d->next) { + for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { if (lfs_pair_cmp(oldpair, d->m.pair) == 0) { d->m.pair[0] = newpair[0]; d->m.pair[1] = newpair[1]; @@ -3371,7 +3405,7 @@ static int lfs_fs_relocate(lfs_t *lfs, // find parent lfs_mdir_t parent; - int32_t tag = lfs_fs_parent(lfs, oldpair, &parent); + lfs_stag_t tag = lfs_fs_parent(lfs, oldpair, &parent); if (tag < 0 && tag != LFS_ERR_NOENT) { return tag; } @@ -3382,7 +3416,7 @@ static int lfs_fs_relocate(lfs_t *lfs, lfs_pair_tole32(newpair); int err = lfs_dir_commit(lfs, &parent, - &(lfs_mattr_t){.tag=tag, .buffer=newpair}); + &(struct lfs_mattr){.tag=tag, .buffer=newpair}); lfs_pair_fromle32(newpair); if (err) { return err; @@ -3431,7 +3465,7 @@ static int lfs_fs_deorphan(lfs_t *lfs) { if (!pdir.split) { // check if we have a parent lfs_mdir_t parent; - int32_t tag = lfs_fs_parent(lfs, pdir.tail, &parent); + lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent); if (tag < 0 && tag != LFS_ERR_NOENT) { return tag; } @@ -3455,7 +3489,7 @@ static int lfs_fs_deorphan(lfs_t *lfs) { } lfs_block_t pair[2]; - int32_t res = lfs_dir_get(lfs, &parent, 0x7ffff000, tag, pair); + lfs_stag_t res = lfs_dir_get(lfs, &parent, 0x7ffff000, tag, pair); if (res < 0) { return res; } @@ -113,7 +113,7 @@ enum lfs_type { LFS_FROM_REGION = 0x000, LFS_FROM_DISK = 0x200, LFS_FROM_MOVE = 0x0c1, - LFS_FROM_ATTRS = 0x0c2, + LFS_FROM_USERATTRS = 0x0c2, LFS_FROM_SUPERBLOCK = 0x0c3, }; @@ -284,13 +284,7 @@ struct lfs_file_config { }; -/// littlefs data structures /// -typedef struct lfs_mattr { - int32_t tag; - const void *buffer; - const struct lfs_mattr *next; -} lfs_mattr_t; - +/// internal littlefs data structures /// typedef struct lfs_cache { lfs_block_t block; lfs_off_t off; @@ -324,13 +318,7 @@ typedef struct lfs_mdir { lfs_global_t locals; } lfs_mdir_t; -typedef struct lfs_mlist { - struct lfs_mlist *next; - uint16_t id; - uint8_t type; - lfs_mdir_t m; -} lfs_mlist_t; - +// littlefs directory type typedef struct lfs_dir { struct lfs_dir *next; uint16_t id; @@ -341,6 +329,7 @@ typedef struct lfs_dir { lfs_block_t head[2]; } lfs_dir_t; +// littlefs file type typedef struct lfs_file { struct lfs_file *next; uint16_t id; @@ -373,26 +362,29 @@ typedef struct lfs_superblock { lfs_size_t inline_max; } lfs_superblock_t; -typedef struct lfs_free { - lfs_block_t off; - lfs_block_t size; - lfs_block_t i; - lfs_block_t ack; - uint32_t *buffer; -} lfs_free_t; - -// The littlefs type +// The littlefs filesystem type typedef struct lfs { lfs_cache_t rcache; lfs_cache_t pcache; lfs_block_t root[2]; - lfs_mlist_t *mlist; + struct lfs_mlist { + struct lfs_mlist *next; + uint16_t id; + uint8_t type; + lfs_mdir_t m; + } *mlist; uint32_t seed; lfs_global_t globals; lfs_global_t locals; - lfs_free_t free; + struct lfs_free { + lfs_block_t off; + lfs_block_t size; + lfs_block_t i; + lfs_block_t ack; + uint32_t *buffer; + } free; const struct lfs_config *cfg; lfs_size_t block_size; |