diff options
author | Christopher Haster <chaster@utexas.edu> | 2018-12-29 16:53:12 +0300 |
---|---|---|
committer | Christopher Haster <chaster@utexas.edu> | 2019-01-14 08:56:01 +0300 |
commit | b989b4a89f6c69b0219b64404de89be63923ca44 (patch) | |
tree | 03a004da63667df2d3490704877647a3fd51cbfc | |
parent | a548ce68c116cb2ac67c266e4a4b1aa9fb3f8e45 (diff) |
Cleaned up tag encoding, now with clear chunk field
Before, the tag format's type field was limited to 9-bits. This sounds
like a lot, but this field needed to encode up to 256 user-specified
types. This limited the flexibility of the encoded types. As time went
on, more bits in the type field were repurposed for various things,
leaving a rather fragile type field.
Here we make the jump to full 11-bit type fields. This comes at the cost
of a smaller length field, however the use of the length field was
always going to come with a RAM limitation. Rather than putting pressure
on RAM for inline files, the new type field lets us encode a chunk
number, splitting up inline files into multiple updatable units. This
actually pushes the theoretical inline max from 8KiB to 256KiB! (Note
that we only allow a single 1KiB chunk for now, chunky inline files
is just a theoretical future improvement).
Here is the new 32-bit tag format, note that there are multiple levels
of types which break down into more info:
[---- 32 ----]
[1|-- 11 --|-- 10 --|-- 10 --]
^. ^ . ^ ^- entry length
|. | . \------------ file id chunk info
|. \-----.------------------ type info (type3)
\.-----------.------------------ valid bit
[-3-|-- 8 --]
^ ^- chunk info
\------- type info (type1)
Additionally, I've split the CREATE tag into separate SPLICE and NAME
tags. This simplified the new compact logic a bit. For now, littlefs
still follows the rule that a NAME tag precedes any other tags related
to a file, but this can change in the future.
-rw-r--r-- | lfs.c | 733 | ||||
-rw-r--r-- | lfs.h | 58 | ||||
-rwxr-xr-x | tests/corrupt.py | 2 | ||||
-rwxr-xr-x | tests/debug.py | 54 | ||||
-rwxr-xr-x | tests/test_alloc.sh | 6 | ||||
-rwxr-xr-x | tests/test_format.sh | 3 |
6 files changed, 411 insertions, 445 deletions
@@ -112,6 +112,12 @@ static int lfs_bd_read(lfs_t *lfs, return 0; } +enum { + LFS_CMP_EQ = 0, + LFS_CMP_LT = 1, + LFS_CMP_GT = 2, +}; + static int lfs_bd_cmp(lfs_t *lfs, const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, lfs_block_t block, lfs_off_t off, @@ -128,11 +134,11 @@ static int lfs_bd_cmp(lfs_t *lfs, } if (dat != data[i]) { - return (dat < data[i]) ? 1 : 2; + return (dat < data[i]) ? LFS_CMP_LT : LFS_CMP_GT; } } - return 0; + return LFS_CMP_EQ; } static int lfs_bd_flush(lfs_t *lfs, @@ -152,8 +158,12 @@ static int lfs_bd_flush(lfs_t *lfs, int res = lfs_bd_cmp(lfs, NULL, rcache, diff, pcache->block, pcache->off, pcache->buffer, diff); - if (res) { - return (res < 0) ? res : LFS_ERR_CORRUPT; + if (res < 0) { + return res; + } + + if (res != LFS_CMP_EQ) { + return LFS_ERR_CORRUPT; } } @@ -268,34 +278,38 @@ typedef uint32_t lfs_tag_t; typedef int32_t lfs_stag_t; #define LFS_MKTAG(type, id, size) \ - (((lfs_tag_t)(type) << 22) | ((lfs_tag_t)(id) << 13) | (lfs_tag_t)(size)) + (((lfs_tag_t)(type) << 20) | ((lfs_tag_t)(id) << 10) | (lfs_tag_t)(size)) static inline bool lfs_tag_isvalid(lfs_tag_t tag) { return !(tag & 0x80000000); } -static inline bool lfs_tag_isuser(lfs_tag_t tag) { - return (tag & 0x40000000); +static inline bool lfs_tag_isdelete(lfs_tag_t tag) { + return ((int32_t)(tag << 22) >> 22) == -1; } -static inline bool lfs_tag_isdelete(lfs_tag_t tag) { - return ((int32_t)(tag << 19) >> 19) == -1; +static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { + return (tag & 0x70000000) >> 20; +} + +static inline uint16_t lfs_tag_type3(lfs_tag_t tag) { + return (tag & 0x7ff00000) >> 20; } -static inline uint16_t lfs_tag_type(lfs_tag_t tag) { - return (tag & 0x7fc00000) >> 22; +static inline uint8_t lfs_tag_chunk(lfs_tag_t tag) { + return (tag & 0x0ff00000) >> 20; } -static inline uint16_t lfs_tag_subtype(lfs_tag_t tag) { - return ((tag & 0x78000000) >> 26) << 4; +static inline int8_t lfs_tag_splice(lfs_tag_t tag) { + return (int8_t)lfs_tag_chunk(tag); } static inline uint16_t lfs_tag_id(lfs_tag_t tag) { - return (tag & 0x003fe000) >> 13; + return (tag & 0x000ffc00) >> 10; } static inline lfs_size_t lfs_tag_size(lfs_tag_t tag) { - return tag & 0x00001fff; + return tag & 0x000003ff; } static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { @@ -405,7 +419,7 @@ static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) { static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, const struct lfs_mattr *attrs); static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, + lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, lfs_mdir_t *source, uint16_t begin, uint16_t end); static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_mdir_t *pdir); @@ -481,114 +495,67 @@ static void lfs_alloc_ack(lfs_t *lfs) { /// Metadata pair and directory operations /// -static int lfs_dir_traverseget(lfs_t *lfs, - const lfs_mdir_t *dir, const struct lfs_mattr *attrs, - lfs_tag_t getmask, lfs_tag_t gettag, lfs_stag_t getdiff, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - lfs_block_t block = dir->pair[0]; +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) { lfs_off_t off = dir->off; lfs_tag_t ntag = dir->etag; - gettag += getdiff; + lfs_stag_t gdiff = 0; + + if (lfs->globals.hasmove && + lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0 && + lfs_tag_id(gtag) <= lfs->globals.id) { + // synthetic moves + gdiff -= LFS_MKTAG(0, 1, 0); + } // 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; - attrs = attrs->next; - } else { - off -= lfs_tag_dsize(ntag); + while (off >= sizeof(lfs_tag_t) + lfs_tag_dsize(ntag)) { + off -= lfs_tag_dsize(ntag); + lfs_tag_t tag = ntag; + int err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, sizeof(ntag), + dir->pair[0], off, &ntag, sizeof(ntag)); + if (err) { + return err; + } - tag = ntag; - buffer = &disk; - disk.block = block; - disk.off = off + sizeof(tag); + ntag = (lfs_frombe32(ntag) ^ tag) & 0x7fffffff; - int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, sizeof(ntag), - block, off, &ntag, sizeof(ntag)); - if (err) { - return err; + if (lfs_tag_id(gmask) != 0 && + lfs_tag_type1(tag) == LFS_TYPE_SPLICE && + lfs_tag_id(tag) <= lfs_tag_id(gtag - gdiff)) { + if (tag == (LFS_MKTAG(LFS_TYPE_CREATE, 0, 0) | + (LFS_MKTAG(0, 0x3ff, 0) & (gtag - gdiff)))) { + // found where we were created + return LFS_ERR_NOENT; } - ntag = lfs_frombe32(ntag) ^ tag; - tag |= 0x80000000; + // move around splices + gdiff += LFS_MKTAG(0, lfs_tag_splice(tag), 0); } - if (lfs_tag_id(getmask) != 0 && - 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 ((gmask & tag) == (gmask & (gtag - gdiff))) { if (lfs_tag_isdelete(tag)) { return LFS_ERR_NOENT; } - return cb(data, tag + getdiff, buffer); - } - - if (lfs_tag_id(getmask) != 0 && - lfs_tag_subtype(tag) == LFS_TYPE_CREATE) { - // 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); + lfs_size_t diff = lfs_min(lfs_tag_size(tag), + lfs_tag_size(gtag)); + err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, diff, + dir->pair[0], off+sizeof(tag), buffer, diff); + if (err) { + return err; } - } - } - - return LFS_ERR_NOENT; -} -struct lfs_dir_get_read { - lfs_t *lfs; - void *buffer; - lfs_size_t size; -}; + memset((uint8_t*)buffer + diff, 0, + lfs_tag_size(gtag) - diff); -static int lfs_dir_get_read(void *data, - lfs_tag_t tag, const void *buffer) { - struct lfs_dir_get_read *get = data; - lfs_t *lfs = get->lfs; - const struct lfs_diskoff *disk = buffer; - 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; + return tag + gdiff; } - - memset((uint8_t*)get->buffer + diff, 0, get->size - diff); } - return tag & 0x7fffffff; -} - -static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_tag_t getmask, lfs_tag_t gettag, void *buffer) { - lfs_stag_t getdiff = 0; - if (lfs->globals.hasmove && - lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0 && - lfs_tag_id(gettag) <= lfs->globals.id) { - // synthetic moves - gettag += LFS_MKTAG(0, 1, 0); - getdiff -= LFS_MKTAG(0, 1, 0); - } - - return lfs_dir_traverseget(lfs, dir, NULL, getmask, gettag, getdiff, - lfs_dir_get_read, &(struct lfs_dir_get_read){ - lfs, buffer, lfs_tag_size(gettag)}); + return LFS_ERR_NOENT; } static int lfs_dir_traverse_filter(void *p, @@ -597,46 +564,36 @@ static int lfs_dir_traverse_filter(void *p, (void)buffer; // check for redundancy - lfs_tag_t mask = (lfs_tag_isuser(tag) ? 0x7fffe000 : 0x783fe000); - if (!lfs_tag_subtype(tag) == LFS_TYPE_CREATE && ( - (mask & tag) == (mask & *filtertag) || - (lfs_tag_subtype(tag) == LFS_TYPE_DELETE && - lfs_tag_id(tag) == lfs_tag_id(*filtertag)))) { + uint32_t mask = LFS_MKTAG(0x7ff, 0x3ff, 0); + if ((mask & tag) == (mask & *filtertag) || + (mask & tag) == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | + (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { return true; } // check if we need to adjust for created/deleted tags - if (lfs_tag_subtype(tag) == LFS_TYPE_CREATE && + if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE && lfs_tag_id(tag) <= lfs_tag_id(*filtertag)) { - *filtertag += LFS_MKTAG(0, 1, 0); - } else if (lfs_tag_subtype(tag) == LFS_TYPE_DELETE && - lfs_tag_id(tag) < lfs_tag_id(*filtertag)) { - *filtertag -= LFS_MKTAG(0, 1, 0); + *filtertag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); } return false; } -static int lfs_dir_traverseall(lfs_t *lfs, const lfs_mdir_t *dir, - const struct lfs_mattr *attrs, - lfs_off_t off, lfs_tag_t ptag, int i, - uint16_t begin, uint16_t end, lfs_stag_t diff, +static int lfs_dir_traverse(lfs_t *lfs, + const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag, + const struct lfs_mattr *attrs, int attrcount, + lfs_tag_t tmask, lfs_tag_t ttag, + uint16_t begin, uint16_t end, int16_t diff, int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // precompute length of attrs so we can iterate backwards, this - // lets us "append" commits - int attrcount = 0; - for (const struct lfs_mattr *a = attrs; a; a = a->next) { - attrcount += 1; - } - - // iterate over directory and attrs - while (off+lfs_tag_dsize(ptag) < dir->off || i < attrcount) { + // iterate over directory and attrs, we iterate over attrs in reverse order + // which lets us "append" commits + while (off+lfs_tag_dsize(ptag) < dir->off || attrcount > 0) { lfs_tag_t tag; const void *buffer; struct lfs_diskoff disk; 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), dir->pair[0], off, &tag, sizeof(tag)); @@ -651,27 +608,27 @@ static int lfs_dir_traverseall(lfs_t *lfs, const lfs_mdir_t *dir, ptag = tag; } else { const struct lfs_mattr *a = attrs; - for (int j = 0; j < attrcount-i-1; j++) { + for (int j = 0; j < attrcount-1; j++) { a = a->next; } tag = a->tag; buffer = a->buffer; - i += 1; + attrcount -= 1; + } + + lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); + if ((mask & tmask & tag) != (mask & tmask & ttag)) { + continue; } // do we need to filter? inlining the filtering logic here allows // for some minor optimizations - if (end <= 0x1ff) { - if (!lfs_tag_isuser(tag) && - lfs_tag_subtype(tag) != LFS_TYPE_STRUCT && - lfs_tag_subtype(tag) != LFS_TYPE_FROM) { - continue; - } - + if (lfs_tag_id(tmask) != 0) { // scan for duplicates and update tag based on creates/deletes - int filter = lfs_dir_traverseall(lfs, dir, attrs, - off, ptag, i, 0, -1, 0, + int filter = lfs_dir_traverse(lfs, + dir, off, ptag, attrs, attrcount, + 0, 0, 0, 0, 0, lfs_dir_traverse_filter, &tag); if (filter < 0) { return filter; @@ -688,27 +645,28 @@ static int lfs_dir_traverseall(lfs_t *lfs, const lfs_mdir_t *dir, } // handle special cases for mcu-side operations - if (lfs_tag_type(tag) == LFS_FROM_MOVE) { + if (lfs_tag_type3(tag) == LFS_FROM_MOVE) { uint16_t fromid = lfs_tag_size(tag); uint16_t toid = lfs_tag_id(tag); - int err = lfs_dir_traverseall(lfs, buffer, NULL, - 0, 0xffffffff, 0, fromid, fromid+1, - LFS_MKTAG(0, toid - fromid, 0) + diff, + int err = lfs_dir_traverse(lfs, + buffer, 0, 0xffffffff, NULL, 0, + LFS_MKTAG(0x600, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), + fromid, fromid+1, toid-fromid+diff, cb, data); if (err) { return err; } - } else if (lfs_tag_type(tag) == LFS_FROM_USERATTRS) { + } else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) { for (const struct lfs_attr *a = buffer; a; a = a->next) { - int err = cb(data, - LFS_MKTAG(0x100 | a->type, lfs_tag_id(tag), a->size) - + diff, a->buffer); + int err = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a->type, + lfs_tag_id(tag) + diff, a->size), a->buffer); if (err) { return err; } } } else { - int err = cb(data, tag + diff, buffer); + int err = cb(data, tag + LFS_MKTAG(0, diff, 0), buffer); if (err) { return err; } @@ -718,30 +676,14 @@ static int lfs_dir_traverseall(lfs_t *lfs, const lfs_mdir_t *dir, return 0; } -static int lfs_dir_traverse(lfs_t *lfs, const lfs_mdir_t *dir, - const struct lfs_mattr *attrs, - uint16_t begin, uint16_t end, lfs_stag_t diff, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // grab create tags first, this is needed because we can't really order - // tags by ids otherwise, and our insertion cleverness becomes a problem - for (uint16_t i = begin; i < end; i++) { - int err = lfs_dir_traverseget(lfs, dir, attrs, - 0x783fe000, LFS_MKTAG(LFS_TYPE_CREATE, i, 0), - diff, cb, data); - if (err) { - return err; - } - } - - // then just grab every other tag in range, order is no longer a concern - return lfs_dir_traverseall(lfs, dir, attrs, - 0, 0xffffffff, 0, begin, end, diff, cb, data); -} - 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, + lfs_tag_t fmask, lfs_tag_t ftag, uint16_t *id, int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { + // we can find tag very efficiently during a fetch, since we're already + // scanning the entire directory + lfs_stag_t besttag = -1; + // find the block with the most recent revision uint32_t revs[2]; int r = 0; @@ -759,41 +701,32 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, } } - // now fetch the actual dir (and find match) - lfs_stag_t foundtag = 0; - dir->pair[0] = pair[0]; - dir->pair[1] = pair[1]; - dir->off = 0; - if (r != 0) { - lfs_pair_swap(dir->pair); - lfs_pair_swap(revs); - } + dir->pair[0] = pair[(r+0)%2]; + dir->pair[1] = pair[(r+1)%2]; + dir->rev = revs[(r+0)%2]; + dir->off = 0; // nonzero = found some commits - // scan tags and check crcs + // now scan tags to fetch the actual dir and find possible match for (int i = 0; i < 2; i++) { - lfs_block_t block = dir->pair[0]; - lfs_off_t off = sizeof(uint32_t); + lfs_off_t off = 0; 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, - }; + uint16_t tempcount = 0; + lfs_block_t temptail[2] = {0xffffffff, 0xffffffff}; + bool tempsplit = false; + lfs_stag_t tempbesttag = besttag; - temp.rev = lfs_tole32(temp.rev); - uint32_t crc = lfs_crc(0xffffffff, &temp.rev, sizeof(temp.rev)); - temp.rev = lfs_fromle32(temp.rev); + dir->rev = lfs_tole32(dir->rev); + uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); + dir->rev = lfs_fromle32(dir->rev); while (true) { // extract next tag lfs_tag_t tag; + off += lfs_tag_dsize(ptag); int err = lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - block, off, &tag, sizeof(tag)); + dir->pair[0], off, &tag, sizeof(tag)); if (err) { if (err == LFS_ERR_CORRUPT) { // can't continue? @@ -806,24 +739,21 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, crc = lfs_crc(crc, &tag, sizeof(tag)); tag = lfs_frombe32(tag) ^ ptag; - // next commit not yet programmed - if (!lfs_tag_isvalid(tag)) { - dir->erased = (lfs_tag_subtype(ptag) == LFS_TYPE_CRC); + // next commit not yet programmed or we're not in valid range + if (!lfs_tag_isvalid(tag) || + off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { + dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC); break; } - // check we're in valid range - if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { - dir->erased = false; - break; - } + ptag = tag; - if (lfs_tag_subtype(tag) == LFS_TYPE_CRC) { + if (lfs_tag_type1(tag) == LFS_TYPE_CRC) { // check the crc attr uint32_t dcrc; err = lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - block, off+sizeof(tag), &dcrc, sizeof(dcrc)); + dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); if (err) { if (err == LFS_ERR_CORRUPT) { dir->erased = false; @@ -839,113 +769,127 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, } // reset the next bit if we need to - tag ^= (lfs_tag_type(tag) & 1) << 31; + ptag ^= (lfs_tag_chunk(tag) & 1) << 31; + + // toss our crc into the filesystem seed for + // pseudorandom numbers lfs->seed ^= crc; - crc = 0xffffffff; // update with what's found so far - foundtag = tempfoundtag; - *dir = temp; + besttag = tempbesttag; dir->off = off + lfs_tag_dsize(tag); - dir->etag = tag; - } 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, - block, off+j, &dat, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } + dir->etag = ptag; + dir->count = tempcount; + dir->tail[0] = temptail[0]; + dir->tail[1] = temptail[1]; + dir->split = tempsplit; - crc = lfs_crc(crc, &dat, 1); - } - - // check for special tags - if (lfs_tag_subtype(tag) == LFS_TYPE_CREATE) { - temp.count += 1; + // reset crc + crc = 0xffffffff; + continue; + } - if (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(temp.count > 0); - temp.count -= 1; - - if (tempfoundtag && !lfs_tag_isdelete(tempfoundtag) && - lfs_tag_id(tag) == lfs_tag_id(tempfoundtag)) { - tempfoundtag = 0; - } else if (tempfoundtag && - lfs_tag_id(tag) < lfs_tag_id(tempfoundtag)) { - tempfoundtag -= LFS_MKTAG(0, 1, 0); - } - } else if (lfs_tag_subtype(tag) == LFS_TYPE_TAIL) { - temp.split = (lfs_tag_type(tag) & 1); - err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - block, off+sizeof(tag), - &temp.tail, sizeof(temp.tail)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } + // crc the entry first, hopefully 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); + if (err) { + if (err == LFS_ERR_CORRUPT) { + dir->erased = false; + break; } - lfs_pair_fromle32(temp.tail); + return err; } - if ((tag & matchmask) == (matchtag & matchmask)) { - // found a match? - if (lfs_tag_isdelete(tag)) { - tempfoundtag = 0; - } 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; - break; - } - return res; - } + crc = lfs_crc(crc, &dat, 1); + } - if (res && (!tempfoundtag || - lfs_tag_id(res) <= lfs_tag_id(tempfoundtag))) { - tempfoundtag = res; - } + // directory modification tags? + if (lfs_tag_type1(tag) == LFS_TYPE_NAME) { + // increase count of files if necessary + if (lfs_tag_id(tag) >= tempcount) { + tempcount = lfs_tag_id(tag) + 1; + } + } else if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE) { + tempcount += lfs_tag_splice(tag); + + if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | + (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) { + tempbesttag |= 0x80000000; + } else if (tempbesttag != -1 && + lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { + tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); + } + } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { + tempsplit = (lfs_tag_chunk(tag) & 1); + err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, + dir->pair[0], off+sizeof(tag), + &temptail, sizeof(temptail)); + if (err) { + if (err == LFS_ERR_CORRUPT) { + dir->erased = false; + break; } } + lfs_pair_fromle32(temptail); } - ptag = tag; - off += lfs_tag_dsize(tag); + // found a match for our fetcher? + if ((fmask & tag) == (fmask & ftag)) { + int res = cb(data, tag, &(struct lfs_diskoff){ + dir->pair[0], off+sizeof(tag)}); + if (res < 0) { + if (res == LFS_ERR_CORRUPT) { + dir->erased = false; + break; + } + return res; + } + + if (res == LFS_CMP_EQ) { + // found a match + tempbesttag = tag; + } else if (res == LFS_CMP_GT && + lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { + // found a greater match, keep track to keep things sorted + tempbesttag = tag | 0x80000000; + } + } } // consider what we have good enough if (dir->off > 0) { // synthetic move - if (foundtag && - lfs->globals.hasmove && + if (lfs->globals.hasmove && lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) { - if (lfs->globals.id == lfs_tag_id(foundtag)) { - foundtag = 0; - } else if (lfs->globals.id < lfs_tag_id(foundtag)) { - foundtag -= LFS_MKTAG(0, 1, 0); + if (lfs->globals.id == lfs_tag_id(besttag)) { + besttag |= 0x80000000; + } else if (besttag != -1 && + lfs->globals.id < lfs_tag_id(besttag)) { + besttag -= LFS_MKTAG(0, 1, 0); } } - return foundtag; + // found tag? or found best id? + if (id) { + *id = lfs_min(lfs_tag_id(besttag), dir->count); + } + + if (lfs_tag_isvalid(besttag)) { + return besttag; + } else if (lfs_tag_id(besttag) < dir->count) { + return LFS_ERR_NOENT; + } else { + return 0; + } } - // failed, try the other crc? + // failed, try the other block? lfs_pair_swap(dir->pair); - lfs_pair_swap(revs); + dir->rev = revs[(r+1)%2]; } LFS_ERROR("Corrupted dir pair at %"PRIu32" %"PRIu32, @@ -955,22 +899,23 @@ static lfs_stag_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]) { - return lfs_dir_fetchmatch(lfs, dir, pair, - 0xffffffff, 0x00000000, NULL, NULL); + // note, mask=-1, tag=0 can never match a tag since this + // pattern has the invalid bit set + return lfs_dir_fetchmatch(lfs, dir, pair, -1, 0, NULL, NULL, NULL); } static int lfs_dir_getglobals(lfs_t *lfs, const lfs_mdir_t *dir, struct lfs_globals *globals) { struct lfs_globals locals; - lfs_stag_t res = lfs_dir_get(lfs, dir, 0x78000000, + lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0, 0), LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals); if (res < 0 && res != LFS_ERR_NOENT) { return res; } if (res != LFS_ERR_NOENT) { - locals.hasmove = (lfs_tag_type(res) & 2); - locals.orphans = (lfs_tag_type(res) & 1); + locals.hasmove = (lfs_tag_type3(res) & 2); + locals.orphans = (lfs_tag_type3(res) & 1); // xor together to find resulting globals lfs_global_xor(globals, &locals); } @@ -980,32 +925,32 @@ static int lfs_dir_getglobals(lfs_t *lfs, const lfs_mdir_t *dir, static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir, uint16_t id, struct lfs_info *info) { - if (id == 0x1ff) { + if (id == 0x3ff) { // special case for root strcpy(info->name, "/"); info->type = LFS_TYPE_DIR; return 0; } - lfs_stag_t tag = lfs_dir_get(lfs, dir, 0x7c3fe000, - LFS_MKTAG(LFS_TYPE_DIR, id, lfs->name_max+1), info->name); + lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name); if (tag < 0) { return tag; } - info->type = lfs_tag_type(tag); + info->type = lfs_tag_type3(tag); struct lfs_ctz ctz; - tag = lfs_dir_get(lfs, dir, 0x783fe000, + tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); if (tag < 0) { return tag; } lfs_ctz_fromle32(&ctz); - if (lfs_tag_type(tag) == LFS_TYPE_CTZSTRUCT) { + if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { info->size = ctz.size; - } else if (lfs_tag_type(tag) == LFS_TYPE_INLINESTRUCT) { + } else if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { info->size = lfs_tag_size(tag); } @@ -1024,35 +969,34 @@ static int lfs_dir_find_match(void *data, lfs_t *lfs = name->lfs; const struct lfs_diskoff *disk = buffer; + // compare with disk lfs_size_t diff = lfs_min(name->size, lfs_tag_size(tag)); int res = lfs_bd_cmp(lfs, NULL, &lfs->rcache, diff, disk->block, disk->off, name->name, diff); - if (res < 0) { + if (res != LFS_CMP_EQ) { return res; } - // found match? - if (res == 0 && name->size == lfs_tag_size(tag)) { - return tag; + // only equal if our size is still the same + if (name->size != lfs_tag_size(tag)) { + return (name->size < lfs_tag_size(tag)) ? LFS_CMP_LT : LFS_CMP_GT; } - // a greater name found, exit early - if (res > 1 && lfs_tag_type(tag) != LFS_TYPE_SUPERBLOCK) { - return tag | 0x1fff; - } - - // no match keep looking - return 0; + // found a match! + return LFS_CMP_EQ; } static int lfs_dir_find(lfs_t *lfs, lfs_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 - lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x1ff, 0); + lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); dir->tail[0] = lfs->root[0]; dir->tail[1] = lfs->root[1]; @@ -1102,13 +1046,13 @@ nextname: *path = name; // only continue if we hit a directory - if (lfs_tag_type(tag) != LFS_TYPE_DIR) { + if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { return LFS_ERR_NOTDIR; } // grab the entry data - if (lfs_tag_id(tag) != 0x1ff) { - lfs_stag_t res = lfs_dir_get(lfs, dir, 0x783fe000, + if (lfs_tag_id(tag) != 0x3ff) { + lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail); if (res < 0) { return res; @@ -1119,30 +1063,21 @@ nextname: // find entry matching name while (true) { tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, - 0x7c000000, LFS_MKTAG(LFS_TYPE_DIR, 0, namelen), + LFS_MKTAG(0x780, 0, 0), + LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), + // are we last name? + (strchr(name, '/') == NULL) ? id : NULL, lfs_dir_find_match, &(struct lfs_dir_find_match){ lfs, name, namelen}); if (tag < 0) { return tag; } - if (id) { - if (strchr(name, '/') != NULL) { - // if path is not only name we're not valid candidate - // for creation - *id = 0x1ff; - } else if (tag) { - *id = lfs_tag_id(tag); - } else { - *id = dir->count; - } - } - - if (tag && !lfs_tag_isdelete(tag)) { + if (tag) { break; } - if (lfs_tag_isdelete(tag) || !dir->split) { + if (!dir->split) { return LFS_ERR_NOENT; } } @@ -1227,7 +1162,7 @@ static int lfs_dir_commitglobals(lfs_t *lfs, struct lfs_commit *commit, struct lfs_globals *globals) { return lfs_dir_commitattr(lfs, commit, LFS_MKTAG(LFS_TYPE_GLOBALS + 2*globals->hasmove + globals->orphans, - 0x1ff, 10), globals); + 0x3ff, 10), globals); } static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { @@ -1247,7 +1182,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { // build crc tag bool reset = ~lfs_frombe32(tag) >> 31; tag = LFS_MKTAG(LFS_TYPE_CRC + reset, - 0x1ff, off - (commit->off+sizeof(lfs_tag_t))); + 0x3ff, off - (commit->off+sizeof(lfs_tag_t))); // write out crc uint32_t footer[2]; @@ -1347,12 +1282,12 @@ static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, const lfs_mdir_t *tail) { // update pred's tail return lfs_dir_commit(lfs, dir, LFS_MKATTR(LFS_TYPE_TAIL + dir->split, - 0x1ff, dir->tail, sizeof(dir->tail), + 0x3ff, dir->tail, sizeof(dir->tail), NULL)); } static int lfs_dir_split(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, + lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, lfs_mdir_t *source, uint16_t split, uint16_t end) { // create tail directory lfs_mdir_t tail; @@ -1365,7 +1300,7 @@ static int lfs_dir_split(lfs_t *lfs, tail.tail[0] = dir->tail[0]; tail.tail[1] = dir->tail[1]; - err = lfs_dir_compact(lfs, &tail, attrs, source, split, end); + err = lfs_dir_compact(lfs, &tail, attrs, attrcount, source, split, end); if (err) { return err; } @@ -1402,7 +1337,7 @@ static int lfs_dir_commit_commit(void *p, lfs_tag_t tag, const void *buffer) { } static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, + lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, 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]}; @@ -1413,7 +1348,11 @@ static int lfs_dir_compact(lfs_t *lfs, while (true) { // find size lfs_size_t size = 0; - int err = lfs_dir_traverse(lfs, source, attrs, begin, end, 0, + int err = lfs_dir_traverse(lfs, + source, 0, 0xffffffff, attrs, attrcount, + LFS_MKTAG(0x400, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_NAME, 0, 0), + begin, end, -begin, lfs_dir_commit_size, &size); if (err) { return err; @@ -1431,7 +1370,8 @@ static int lfs_dir_compact(lfs_t *lfs, // largest size that fits with a small binary search, but right now // it's not worth the code size uint16_t split = (end - begin) / 2; - err = lfs_dir_split(lfs, dir, attrs, source, begin+split, end); + err = lfs_dir_split(lfs, dir, attrs, attrcount, + source, begin+split, end); if (err) { // if we fail to split, we may be able to overcompact, unless // we're too big for even the full block, in which case our @@ -1460,7 +1400,8 @@ static int lfs_dir_compact(lfs_t *lfs, // by itself, so expand cautiously if ((lfs_size_t)res < lfs->cfg->block_count/2) { LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); - int err = lfs_dir_split(lfs, dir, attrs, source, begin, end); + int err = lfs_dir_split(lfs, dir, attrs, attrcount, + source, begin, end); if (err && err != LFS_ERR_NOSPC) { return err; } @@ -1522,9 +1463,12 @@ static int lfs_dir_compact(lfs_t *lfs, return err; } - // commit with a move - err = lfs_dir_traverse(lfs, source, attrs, - begin, end, -LFS_MKTAG(0, begin, 0), + // traverse the directory, this time writing out all unique tags + err = lfs_dir_traverse(lfs, + source, 0, 0xffffffff, attrs, attrcount, + LFS_MKTAG(0x400, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_NAME, 0, 0), + begin, end, -begin, lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ lfs, &commit}); if (err) { @@ -1553,7 +1497,7 @@ static int lfs_dir_compact(lfs_t *lfs, lfs_pair_tole32(dir->tail); err = lfs_dir_commitattr(lfs, &commit, LFS_MKTAG(LFS_TYPE_TAIL + dir->split, - 0x1ff, sizeof(dir->tail)), dir->tail); + 0x3ff, sizeof(dir->tail)), dir->tail); lfs_pair_fromle32(dir->tail); if (err) { if (err == LFS_ERR_CORRUPT) { @@ -1651,10 +1595,10 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, lfs_tag_t createtag = 0xffffffff; int attrcount = 0; for (const struct lfs_mattr *a = attrs; a; a = a->next) { - if (lfs_tag_subtype(a->tag) == LFS_TYPE_CREATE) { + if (lfs_tag_type3(a->tag) == LFS_TYPE_CREATE) { dir->count += 1; createtag = a->tag; - } else if (lfs_tag_subtype(a->tag) == LFS_TYPE_DELETE) { + } else if (lfs_tag_type3(a->tag) == LFS_TYPE_DELETE) { LFS_ASSERT(dir->count > 0); dir->count -= 1; deletetag = a->tag; @@ -1690,8 +1634,9 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, // traverse attrs that need to be written out lfs_pair_tole32(dir->tail); - int err = lfs_dir_traverseall(lfs, dir, attrs, - dir->off, dir->etag, 0, 0, -1, 0, + int err = lfs_dir_traverse(lfs, + dir, dir->off, dir->etag, attrs, attrcount, + 0, 0, 0, 0, 0, lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ lfs, &commit}); lfs_pair_fromle32(dir->tail); @@ -1740,7 +1685,8 @@ compact: // fall back to compaction lfs_cache_drop(lfs, &lfs->pcache); - int err = lfs_dir_compact(lfs, dir, attrs, dir, 0, dir->count); + int err = lfs_dir_compact(lfs, dir, attrs, attrcount, + dir, 0, dir->count); if (err) { return err; } @@ -1798,7 +1744,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { lfs_mdir_t cwd; uint16_t id; err = lfs_dir_find(lfs, &cwd, &path, &id); - if (!(err == LFS_ERR_NOENT && id != 0x1ff)) { + if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { return (err < 0) ? err : LFS_ERR_EXIST; } @@ -1844,7 +1790,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { pred.tail[1] = dir.pair[1]; lfs_global_orphans(lfs, +1); err = lfs_dir_commit(lfs, &pred, - LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x1ff, + LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pred.tail, sizeof(pred.tail), NULL)); if (err) { @@ -1858,10 +1804,11 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { err = lfs_dir_commit(lfs, &cwd, LFS_MKATTR(LFS_TYPE_DIRSTRUCT, id, dir.pair, sizeof(dir.pair), LFS_MKATTR(LFS_TYPE_DIR, id, path, nlen, + LFS_MKATTR(LFS_TYPE_CREATE, id, NULL, 0, (!cwd.split) - ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x1ff, + ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, cwd.tail, sizeof(cwd.tail), NULL) - : NULL))); + : NULL)))); lfs_pair_fromle32(dir.pair); if (err) { return err; @@ -1876,18 +1823,18 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { return tag; } - if (lfs_tag_type(tag) != LFS_TYPE_DIR) { + if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { return LFS_ERR_NOTDIR; } lfs_block_t pair[2]; - if (lfs_tag_id(tag) == 0x1ff) { + if (lfs_tag_id(tag) == 0x3ff) { // handle root dir separately pair[0] = lfs->root[0]; pair[1] = lfs->root[1]; } else { // get dir pair from parent - lfs_stag_t res = lfs_dir_get(lfs, &dir->m, 0x783fe000, + lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); if (res < 0) { return res; @@ -2238,7 +2185,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, // allocate entry for file if it doesn't exist lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id); - if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x1ff)) { + if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) { err = tag; goto cleanup; } @@ -2265,7 +2212,8 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, err = lfs_dir_commit(lfs, &file->m, LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id, NULL, 0, LFS_MKATTR(LFS_TYPE_REG, file->id, path, nlen, - NULL))); + LFS_MKATTR(LFS_TYPE_CREATE, file->id, NULL, 0, + NULL)))); if (err) { err = LFS_ERR_NAMETOOLONG; goto cleanup; @@ -2275,7 +2223,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, } else if (flags & LFS_O_EXCL) { err = LFS_ERR_EXIST; goto cleanup; - } else if (lfs_tag_type(tag) != LFS_TYPE_REG) { + } else if (lfs_tag_type3(tag) != LFS_TYPE_REG) { err = LFS_ERR_ISDIR; goto cleanup; } else if (flags & LFS_O_TRUNC) { @@ -2284,7 +2232,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, file->flags |= LFS_F_DIRTY; } else { // try to load what's on disk, if it's inlined we'll fix it later - tag = lfs_dir_get(lfs, &file->m, 0x783fe000, + tag = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz); if (tag < 0) { err = tag; @@ -2296,8 +2244,10 @@ 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) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, 0x7fffe000, - LFS_MKTAG(0x100 | a->type, file->id, a->size), a->buffer); + lfs_stag_t res = lfs_dir_get(lfs, &file->m, + LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_USERATTR + a->type, file->id, a->size), + a->buffer); if (res < 0 && res != LFS_ERR_NOENT) { err = res; goto cleanup; @@ -2328,7 +2278,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, // zero to avoid information leak lfs_cache_zero(lfs, &file->cache); - if (lfs_tag_type(tag) == LFS_TYPE_INLINESTRUCT) { + if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { // load inline files file->ctz.head = 0xfffffffe; file->ctz.size = lfs_tag_size(tag); @@ -2339,7 +2289,8 @@ 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) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, 0x783fe000, + 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), file->cache.buffer); if (res < 0) { @@ -2896,10 +2847,10 @@ int lfs_remove(lfs_t *lfs, const char *path) { } lfs_mdir_t dir; - if (lfs_tag_type(tag) == LFS_TYPE_DIR) { + if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { // must be empty before removal lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &cwd, 0x783fe000, + lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); if (res < 0) { return res; @@ -2927,7 +2878,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { return err; } - if (lfs_tag_type(tag) == LFS_TYPE_DIR) { + if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { // fix orphan lfs_global_orphans(lfs, -1); @@ -2963,7 +2914,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { lfs_mdir_t newcwd; uint16_t newid; lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid); - if (prevtag < 0 && !(prevtag == LFS_ERR_NOENT && newid != 0x1ff)) { + if (prevtag < 0 && !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) { return err; } @@ -2974,12 +2925,12 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { if (nlen > lfs->name_max) { return LFS_ERR_NAMETOOLONG; } - } else if (lfs_tag_type(prevtag) != lfs_tag_type(oldtag)) { + } else if (lfs_tag_type3(prevtag) != lfs_tag_type3(oldtag)) { return LFS_ERR_ISDIR; - } else if (lfs_tag_type(prevtag) == LFS_TYPE_DIR) { + } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { // must be empty before removal lfs_block_t prevpair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &newcwd, 0x783fe000, + lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair); if (res < 0) { return res; @@ -3014,10 +2965,11 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { // move over all attributes err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTR(LFS_FROM_MOVE, newid, &oldcwd, lfs_tag_id(oldtag), - LFS_MKATTR(lfs_tag_type(oldtag), newid, newpath, strlen(newpath), + LFS_MKATTR(lfs_tag_type3(oldtag), newid, newpath, strlen(newpath), + LFS_MKATTR(LFS_TYPE_CREATE, newid, NULL, 0, (prevtag != LFS_ERR_NOENT) ? LFS_MKATTR(LFS_TYPE_DELETE, newid, NULL, 0, NULL) - : NULL))); + : NULL)))); if (err) { return err; } @@ -3031,7 +2983,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { } } - if (prevtag != LFS_ERR_NOENT && lfs_tag_type(prevtag) == LFS_TYPE_DIR) { + if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { // fix orphan lfs_global_orphans(lfs, -1); @@ -3058,7 +3010,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, } uint16_t id = lfs_tag_id(tag); - if (id == 0x1ff) { + if (id == 0x3ff) { // special case for root id = 0; int err = lfs_dir_fetch(lfs, &cwd, lfs->root); @@ -3067,8 +3019,9 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, } } - tag = lfs_dir_get(lfs, &cwd, 0x7fffe000, - LFS_MKTAG(0x100 | type, id, lfs_min(size, lfs->attr_max)), + tag = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_USERATTR + type, + id, lfs_min(size, lfs->attr_max)), buffer); if (tag < 0) { if (tag == LFS_ERR_NOENT) { @@ -3089,7 +3042,7 @@ static int lfs_commitattr(lfs_t *lfs, const char *path, } uint16_t id = lfs_tag_id(tag); - if (id == 0x1ff) { + if (id == 0x3ff) { // special case for root id = 0; int err = lfs_dir_fetch(lfs, &cwd, lfs->root); @@ -3099,7 +3052,7 @@ static int lfs_commitattr(lfs_t *lfs, const char *path, } return lfs_dir_commit(lfs, &cwd, - LFS_MKATTR(0x100 | type, id, buffer, size, + LFS_MKATTR(LFS_TYPE_USERATTR + type, id, buffer, size, NULL)); } @@ -3113,7 +3066,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, 0x1fff); + return lfs_commitattr(lfs, path, type, NULL, 0x3ff); } @@ -3268,7 +3221,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { LFS_MKATTR(LFS_TYPE_INLINESTRUCT, 0, &superblock, sizeof(superblock), LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, "littlefs", 8, - NULL))); + LFS_MKATTR(LFS_TYPE_CREATE, 0, NULL, 0, + NULL)))); if (err) { goto cleanup; } @@ -3295,8 +3249,10 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { lfs_mdir_t dir = {.tail = {0, 1}}; while (!lfs_pair_isnull(dir.tail)) { // fetch next block in tail list - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, 0x7fffe000, + lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, + LFS_MKTAG(0x7ff, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), + NULL, lfs_dir_find_match, &(struct lfs_dir_find_match){ lfs, "littlefs", 8}); if (tag < 0) { @@ -3312,7 +3268,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { // grab superblock lfs_superblock_t superblock; - tag = lfs_dir_get(lfs, &dir, 0x7fffe000, + tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), &superblock); if (tag < 0) { @@ -3439,7 +3395,7 @@ int lfs_fs_traverse(lfs_t *lfs, for (uint16_t id = 0; id < dir.count; id++) { struct lfs_ctz ctz; - lfs_stag_t tag = lfs_dir_get(lfs, &dir, 0x783fe000, + lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); if (tag < 0) { if (tag == LFS_ERR_NOENT) { @@ -3449,7 +3405,7 @@ int lfs_fs_traverse(lfs_t *lfs, } lfs_ctz_fromle32(&ctz); - if (lfs_tag_type(tag) == LFS_TYPE_CTZSTRUCT) { + if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, ctz.head, ctz.size, cb, data); if (err) { @@ -3525,7 +3481,7 @@ static int lfs_fs_parent_match(void *data, } lfs_pair_fromle32(child); - return (lfs_pair_cmp(child, find->pair) == 0) ? tag : 0; + return (lfs_pair_cmp(child, find->pair) == 0) ? LFS_CMP_EQ : LFS_CMP_LT; } static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], @@ -3535,10 +3491,12 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], parent->tail[1] = 1; while (!lfs_pair_isnull(parent->tail)) { lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, - 0x7fc01fff, LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), + LFS_MKTAG(0x7ff, 0, 0x3ff), + LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), + NULL, lfs_fs_parent_match, &(struct lfs_fs_parent_match){ lfs, {pair[0], pair[1]}}); - if (tag) { + if (tag && tag != LFS_ERR_NOENT) { return tag; } } @@ -3600,7 +3558,7 @@ static int lfs_fs_relocate(lfs_t *lfs, parent.tail[1] = newpair[1]; err = lfs_dir_commit(lfs, &parent, LFS_MKATTR(LFS_TYPE_TAIL + parent.split, - 0x1ff, parent.tail, sizeof(parent.tail), + 0x3ff, parent.tail, sizeof(parent.tail), NULL)); if (err) { return err; @@ -3674,7 +3632,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) { } lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &parent, 0x7fffe000, tag, pair); + lfs_stag_t res = lfs_dir_get(lfs, &parent, + LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair); if (res < 0) { return res; } @@ -3689,7 +3648,7 @@ static int lfs_fs_deorphan(lfs_t *lfs) { pdir.tail[1] = pair[1]; err = lfs_dir_commit(lfs, &pdir, LFS_MKATTR(LFS_TYPE_SOFTTAIL, - 0x1ff, pdir.tail, sizeof(pdir.tail), + 0x3ff, pdir.tail, sizeof(pdir.tail), NULL)); if (err) { return err; @@ -3735,5 +3694,5 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs) { return err; } - return size; + return size; } @@ -45,25 +45,25 @@ typedef int32_t lfs_soff_t; typedef uint32_t lfs_block_t; // Maximum name size in bytes, may be redefined to reduce the size of the -// info struct. Limited to <= 8190. Stored in superblock and must be +// info struct. Limited to <= 1022. Stored in superblock and must be // respected by other littlefs drivers. #ifndef LFS_NAME_MAX -#define LFS_NAME_MAX 0xff +#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 <= 8190. Stored in superblock and must +// 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 0x1ffe +#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 <= 8190. Stored +// 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 0x1ffe +#define LFS_ATTR_MAX 1022 #endif // Maximum size of a file in bytes, may be redefined to limit to support other @@ -98,31 +98,33 @@ enum lfs_error { // File types enum lfs_type { // file types - LFS_TYPE_REG = 0x011, - LFS_TYPE_DIR = 0x010, + LFS_TYPE_REG = 0x001, + LFS_TYPE_DIR = 0x002, // internally used types - LFS_TYPE_USERATTR = 0x100, - LFS_TYPE_CREATE = 0x000, - LFS_TYPE_DELETE = 0x020, - LFS_TYPE_STRUCT = 0x040, - LFS_TYPE_TAIL = 0x080, - LFS_TYPE_SOFTTAIL = 0x080, - LFS_TYPE_HARDTAIL = 0x081, - LFS_TYPE_CRC = 0x0a0, - LFS_TYPE_SUPERBLOCK = 0x001, - LFS_TYPE_GLOBALS = 0x0e0, - - LFS_TYPE_DIRSTRUCT = 0x040, - LFS_TYPE_INLINESTRUCT = 0x041, - LFS_TYPE_CTZSTRUCT = 0x042, + LFS_TYPE_SPLICE = 0x400, + LFS_TYPE_NAME = 0x000, + LFS_TYPE_STRUCT = 0x200, + LFS_TYPE_USERATTR = 0x300, + LFS_TYPE_FROM = 0x100, + LFS_TYPE_TAIL = 0x600, + LFS_TYPE_GLOBALS = 0x700, + LFS_TYPE_CRC = 0x500, + + // internally used type specializations + LFS_TYPE_CREATE = 0x401, + LFS_TYPE_DELETE = 0x4ff, + LFS_TYPE_SUPERBLOCK = 0x0ff, + LFS_TYPE_DIRSTRUCT = 0x200, + LFS_TYPE_CTZSTRUCT = 0x202, + LFS_TYPE_INLINESTRUCT = 0x201, + LFS_TYPE_SOFTTAIL = 0x600, + LFS_TYPE_HARDTAIL = 0x601, + LFS_TYPE_MOVESTATE = 0x7ff, // internal chip sources - LFS_TYPE_FROM = 0x060, - LFS_FROM_MEM = 0x000, - LFS_FROM_DISK = 0x200, - LFS_FROM_MOVE = 0x061, - LFS_FROM_USERATTRS = 0x062, + LFS_FROM_MOVE = 0x101, + LFS_FROM_USERATTRS = 0x102, }; // File open flags @@ -314,8 +316,8 @@ typedef struct lfs_cache { typedef struct lfs_mdir { lfs_block_t pair[2]; uint32_t rev; - uint32_t etag; lfs_off_t off; + uint32_t etag; uint16_t count; bool erased; bool split; diff --git a/tests/corrupt.py b/tests/corrupt.py index b6671ff..44f4f66 100755 --- a/tests/corrupt.py +++ b/tests/corrupt.py @@ -19,7 +19,7 @@ def corrupt(block): break tag ^= ntag - size = (tag & 0x1fff) if (tag & 0x1fff) != 0x1fff else 0 + size = (tag & 0x3ff) if (tag & 0x3ff) != 0x3ff else 0 file.seek(size, os.SEEK_CUR) # lob off last 3 bytes diff --git a/tests/debug.py b/tests/debug.py index c76dc3d..f8c0484 100755 --- a/tests/debug.py +++ b/tests/debug.py @@ -4,23 +4,29 @@ import struct import binascii TYPES = { - (0x1ff, 0x011): 'create reg', - (0x1ff, 0x010): 'create dir', - (0x1ff, 0x001): 'superblock', - (0x1ff, 0x020): 'delete', - (0x1f0, 0x0e0): 'globals', - (0x1ff, 0x080): 'tail soft', - (0x1ff, 0x081): 'tail hard', - (0x1f0, 0x0a0): 'crc', - (0x1ff, 0x040): 'struct dir', - (0x1ff, 0x041): 'struct inline', - (0x1ff, 0x042): 'struct ctz', - (0x100, 0x100): 'attr', + (0x700, 0x400): 'splice', + (0x7ff, 0x401): 'create', + (0x7ff, 0x4ff): 'delete', + (0x700, 0x000): 'name', + (0x7ff, 0x001): 'name reg', + (0x7ff, 0x002): 'name dir', + (0x7ff, 0x0ff): 'name superblock', + (0x700, 0x200): 'struct', + (0x7ff, 0x200): 'struct dir', + (0x7ff, 0x202): 'struct ctz', + (0x7ff, 0x201): 'struct inline', + (0x700, 0x300): 'userattr', + (0x700, 0x600): 'tail', + (0x7ff, 0x600): 'tail soft', + (0x7ff, 0x601): 'tail hard', + (0x700, 0x700): 'gstate', + (0x7ff, 0x7ff): 'gstate move', + (0x700, 0x500): 'crc', } def typeof(type): - for prefix in range(9): - mask = 0x1ff & ~((1 << prefix)-1) + for prefix in range(12): + mask = 0x7ff & ~((1 << prefix)-1) if (mask, type & mask) in TYPES: return TYPES[mask, type & mask] + ( ' %0*x' % (prefix/4, type & ((1 << prefix)-1)) @@ -59,7 +65,7 @@ def main(*blocks): print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True)) # go through each tag, print useful information - print "%-4s %-8s %-14s %3s %3s %s" % ( + print "%-4s %-8s %-14s %3s %4s %s" % ( 'off', 'tag', 'type', 'id', 'len', 'dump') tag = 0xffffffff @@ -75,26 +81,26 @@ def main(*blocks): tag ^= ntag off += 4 - type = (tag & 0x7fc00000) >> 22 - id = (tag & 0x003fe000) >> 13 - size = (tag & 0x00001fff) >> 0 - iscrc = (type & 0x1f0) == 0x0f0 + type = (tag & 0x7ff00000) >> 20 + id = (tag & 0x000ffc00) >> 10 + size = (tag & 0x000003ff) >> 0 + iscrc = (type & 0x700) == 0x500 - data = file.read(size if size != 0x1fff else 0) + data = file.read(size if size != 0x3ff else 0) if iscrc: crc = binascii.crc32(data[:4], crc) else: crc = binascii.crc32(data, crc) - print '%04x: %08x %-14s %3s %3s %-23s %-8s' % ( + print '%04x: %08x %-15s %3s %4s %-23s %-8s' % ( off, tag, typeof(type) + (' bad!' if iscrc and ~crc else ''), - id if id != 0x1ff else '.', - size if size != 0x1fff else 'x', + id if id != 0x3ff else '.', + size if size != 0x3ff else 'x', ' '.join('%02x' % ord(c) for c in data[:8]), ''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8])) - off += size if size != 0x1fff else 0 + off += size if size != 0x3ff else 0 if iscrc: crc = 0 tag ^= (type & 1) << 31 diff --git a/tests/test_alloc.sh b/tests/test_alloc.sh index f924a29..3f993e9 100755 --- a/tests/test_alloc.sh +++ b/tests/test_alloc.sh @@ -253,7 +253,7 @@ tests/test.py << TEST // find out max file size lfs_mkdir(&lfs, "exhaustiondir") => 0; - for (int i = 0; i < 9; i++) { + for (int i = 0; i < 10; i++) { sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); lfs_mkdir(&lfs, (char*)buffer) => 0; } @@ -275,7 +275,7 @@ tests/test.py << TEST lfs_remove(&lfs, "exhaustion") => 0; lfs_remove(&lfs, "exhaustiondir") => 0; - for (int i = 0; i < 9; i++) { + for (int i = 0; i < 10; i++) { sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); lfs_remove(&lfs, (char*)buffer) => 0; } @@ -287,7 +287,7 @@ tests/test.py << TEST } lfs_file_sync(&lfs, &file[0]) => 0; - for (int i = 0; i < 9; i++) { + for (int i = 0; i < 10; i++) { sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); lfs_mkdir(&lfs, (char*)buffer) => 0; } diff --git a/tests/test_format.sh b/tests/test_format.sh index d885f83..5a11535 100755 --- a/tests/test_format.sh +++ b/tests/test_format.sh @@ -12,8 +12,7 @@ TEST echo "--- Basic mounting ---" tests/test.py << TEST lfs_format(&lfs, &cfg) => 0; -TEST -tests/test.py << TEST + lfs_mount(&lfs, &cfg) => 0; lfs_unmount(&lfs) => 0; TEST |