From 1cda361df066c85880a8d764da65d72792c01465 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 9 Dec 2020 23:23:20 -0600 Subject: [PATCH] WIP initial commit of radix-based metadata-blocks Currently commit-only --- lfs.c | 773 ++++++++++++++++++++++++++++++++++++++++++ lfs.h | 21 ++ tests/test_mdirs.toml | 38 +++ 3 files changed, 832 insertions(+) create mode 100644 tests/test_mdirs.toml diff --git a/lfs.c b/lfs.c index 4d4db858..eb78be35 100644 --- a/lfs.c +++ b/lfs.c @@ -257,6 +257,65 @@ static int lfs_bd_prog(lfs_t *lfs, return 0; } + +int lfs_bd_prog_(lfs_t *lfs, + lfs_cache_t *pcache, lfs_cache_t *rcache, + lfs_off_t limit, uint32_t *crc, + lfs_block_t block, lfs_off_t off, + const void *buffer, lfs_size_t size) { + const uint8_t *data = buffer; + LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->cfg->block_count); + LFS_ASSERT(limit <= lfs->cfg->block_size); + + // past the end of block? + if (off + size > limit) { + return LFS_ERR_NOSPC; + } + + // crc data as a convenience + if (crc) { + *crc = lfs_crc(*crc, buffer, size); + } + + while (size > 0) { + if (block == pcache->block && + off >= pcache->off && + off < pcache->off + lfs->cfg->cache_size) { + // already fits in pcache? + lfs_size_t diff = lfs_min(size, + lfs->cfg->cache_size - (off-pcache->off)); + memcpy(&pcache->buffer[off-pcache->off], data, diff); + + data += diff; + off += diff; + size -= diff; + + pcache->size = lfs_max(pcache->size, off - pcache->off); + if (pcache->size == lfs->cfg->cache_size) { + // eagerly flush out pcache if we fill up, manually validate if + // we aren't CRCing the data we write (when we CRC we will + // always validate via CRC) + int err = lfs_bd_flush(lfs, pcache, rcache, (crc == NULL)); + if (err) { + return err; + } + } + + continue; + } + + // pcache must have been flushed, either by programming and + // entire block or manually flushing the pcache + LFS_ASSERT(pcache->block == LFS_BLOCK_NULL); + + // prepare pcache, first condition can no longer fail + pcache->block = block; + pcache->off = lfs_aligndown(off, lfs->cfg->prog_size); + pcache->size = 0; + } + + return 0; +} #endif #ifndef LFS_READONLY @@ -268,6 +327,101 @@ static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { } #endif +// leb128 conversion functions, note buffer size expects word-size-bytes +// TODO should these go in lfs_util.h? +lfs_ssize_t lfs_fromleb128_(uint32_t *pword, const void *buffer) { + const uint8_t *bytes = buffer; + uint32_t word = 0; + for (lfs_size_t i = 0; i < 4; i++) { + word |= (bytes[i] & 0x7f) << 7*i; + if (!(bytes[i] & 0x80)) { + *pword = word; + return i+1; + } + } + + LFS_ASSERT(false); + return LFS_ERR_OVERFLOW; +} + +lfs_size_t lfs_toleb128_(void *buffer, uint32_t word) { + uint8_t *bytes = buffer; + for (int i = 0; true; i++) { + uint8_t byte = word & 0x7f; + word >>= 7; + if (word != 0) { + bytes[i] = byte | 0x80; + } else { + bytes[i] = byte | 0x00; + return i+1; + } + } +} + +// TODO is this costly? +uint32_t lfs_fromle32_(const void *buffer) { + uint32_t word; + memcpy(&word, buffer, sizeof(uint32_t)); + return lfs_fromle32(word); +} + +void lfs_tole32_(void *buffer, uint32_t word) { + word = lfs_tole32(word); + memcpy(buffer, &word, sizeof(uint32_t)); +} + +// encode/decode leb128 integers +// TODO modify *off in place instead of returning diff? +// TODO make these static +lfs_ssize_t lfs_bd_readleb128_(lfs_t *lfs, + const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, + lfs_block_t block, lfs_off_t off, uint32_t *pword) { + uint32_t word = 0; + for (int i = 0; true; i++) { + uint8_t byte; + // TODO would making hint = absolute offset make more sense + // with these layers? + int err = lfs_bd_read(lfs, pcache, rcache, hint-i, + block, off+i, &byte, 1); + if (err) { + return err; + } + + // check for overflow + if (i > 4 && (byte & 0x7f) > 0xf) { + return LFS_ERR_OVERFLOW; + } + + word |= (byte & 0x7f) << 7*i; + if (byte & 0x80) { + *pword = word; + return i+1; + } + } +} + +lfs_ssize_t lfs_bd_progleb128_(lfs_t *lfs, + lfs_cache_t *pcache, lfs_cache_t *rcache, + lfs_off_t limit, uint32_t *crc, + lfs_block_t block, lfs_off_t off, uint32_t word) { + for (int i = 0; true; i++) { + uint8_t byte = word & 0x7f; + word >>= 7; + if (word != 0) { + byte |= 0x80; + } + + int err = lfs_bd_prog_(lfs, pcache, rcache, limit, crc, + block, off+i, &byte, 1); + if (err) { + return err; + } + + if (word == 0) { + return i+1; + } + } +} /// Small type-level utilities /// // operations on block pairs @@ -358,12 +512,60 @@ static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { return sizeof(tag) + lfs_tag_size(tag + lfs_tag_isdelete(tag)); } +#define LFS_MKTAG_(type, subtype, id) \ + (((lfs_tag_t)(id) << 12) | ((lfs_tag_t)(subtype) << 4) | (lfs_tag_t)(type)) + +#define LFS_MKALT_(bit) \ + (((lfs_tag_t)(bit) << 1) | 1) + +static inline bool lfs_tag_isvalid_(lfs_tag_t tag) { + return !(tag & 0x80000000); +} + +static inline bool lfs_tag_isalt_(lfs_tag_t tag) { + return (tag & 0x00000001); +} + +static inline bool lfs_tag_isradix_(lfs_tag_t tag) { + return (tag & 0x00000002); +} + +static inline bool lfs_tag_isided_(lfs_tag_t tag) { + return (tag & 0x00000004); +} + +static inline uint8_t lfs_tag_type_(lfs_tag_t tag) { + return tag & 0x0000000f; +} + +static inline uint8_t lfs_tag_subtype_(lfs_tag_t tag) { + return (tag & 0x00000ff0) >> 4; +} + +static inline uint16_t lfs_tag_id_(lfs_tag_t tag) { + return (tag & 0x0ffff000) >> 12; +} + +static inline uint8_t lfs_tag_alt_(lfs_tag_t tag) { + return (tag & 0x0000003e) >> 1; +} + +static inline int16_t lfs_tag_delta_(lfs_tag_t tag) { + return (tag & 0x003fffc0) >> 6; +} + // operations on attributes in attribute lists struct lfs_mattr { lfs_tag_t tag; const void *buffer; }; +struct lfs_mattr_ { + lfs_tag_t tag; + const void *buffer; + lfs_size_t size; +}; + struct lfs_diskoff { lfs_block_t block; lfs_off_t off; @@ -373,6 +575,10 @@ struct lfs_diskoff { (struct lfs_mattr[]){__VA_ARGS__}, \ sizeof((struct lfs_mattr[]){__VA_ARGS__}) / sizeof(struct lfs_mattr) +#define LFS_MKATTRS_(...) \ + (struct lfs_mattr_[]){__VA_ARGS__}, \ + sizeof((struct lfs_mattr_[]){__VA_ARGS__}) / sizeof(struct lfs_mattr_) + // operations on global state static inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) { for (int i = 0; i < 3; i++) { @@ -2131,6 +2337,573 @@ compact: } #endif +//int lfs_dir_fetch_(lfs_t *lfs, lfs_mdir__t *mdir, +// const lfs_block_t pair[2]) { +// // find the block with the most recent revision +// uint32_t revs[2] = {0, 0}; +// int r = 0; +// for (int i = 0; i < 2; i++) { +// int err = lfs_bd_read(lfs, +// NULL, &lfs->rcache, sizeof(revs[i]), +// pair[i], 0, &revs[i], sizeof(revs[i])); +// revs[i] = lfs_fromle32_(&revs[i]); +// if (err && err != LFS_ERR_CORRUPT) { +// return err; +// } +// +// if (err != LFS_ERR_CORRUPT && +// lfs_scmp(revs[i], revs[(i+1)%2]) > 0) { +// r = i; +// } +// +// } +//} + +//// find first different bit +//lfs_tag_t delta = (tag ^ rtag) & ((1 << (28-i))-1); +//printf("tag 0x%08x rtag 0x%08x mask 0x%08x delta 0x%08x\n", tag, rtag, ((1 << (28-i))-1), delta); +//if (delta == 0) { +// break; +//} +// +//i = 28-lfs_npw2(delta+1); + +// TODO should we just take mdir pointer? to a copy? we're carrying +// around a lot of state +lfs_ssize_t lfs_dir_commitattr_(lfs_t *lfs, lfs_off_t *proff, + lfs_off_t limit, uint32_t *crc, lfs_block_t block, lfs_off_t off, + lfs_tag_t tag, const void *buffer, lfs_size_t size) { + lfs_size_t diff = 0; + bool fromdisk = tag & 0x80000000; + tag = tag & 0x7fffffff; + + // write out tag+size + uint8_t header[8]; + lfs_size_t hdiff = 0; + hdiff += lfs_toleb128_(&header[hdiff], tag); + hdiff += lfs_toleb128_(&header[hdiff], size); + int err = lfs_bd_prog_(lfs, + &lfs->pcache, &lfs->rcache, limit, crc, + block, off+diff, &header, hdiff); + if (err) { + return err; + } + diff += hdiff; + + if (!fromdisk) { + // from memory + err = lfs_bd_prog_(lfs, + &lfs->pcache, &lfs->rcache, limit, crc, + block, off+diff, buffer, size); + if (err) { + return err; + } + diff += size; + } else { + // from disk + const struct lfs_diskoff *disk = buffer; + for (lfs_off_t i = 0; i < size; i++) { + // rely on caching to make this efficient + uint8_t dat; + err = lfs_bd_read(lfs, + NULL, &lfs->rcache, size-i, + disk->block, disk->off+i, &dat, 1); + if (err) { + return err; + } + + // TODO do we still need to invalidate rcache during + // commit process? + err = lfs_bd_prog_(lfs, + &lfs->pcache, &lfs->rcache, limit, crc, + block, off+diff+i, &dat, 1); + if (err) { + return err; + } + } + diff += size; + } + + // perform radix walk for alt-pointers? + if (lfs_tag_isradix_(tag)) { + printf("finding radices for 0x%08x!\n", tag); + + lfs_off_t roff = *proff; + // tags use only the lower 28-bits + for (int i = 0; i < 28 && roff; i++) { + // first get tag + // TODO consolidate into function + hdiff = 0; + err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, sizeof(header), + block, roff, &header, sizeof(header)); + if (err) { + return err; + } + + lfs_tag_t rtag; + lfs_ssize_t res = lfs_fromleb128_(&rtag, &header[0]); + if (res < 0) { + return res; + } + hdiff += res; + + lfs_tag_t rsize; + res = lfs_fromleb128_(&rsize, &header[hdiff]); + if (res < 0) { + return res; + } + hdiff += res; + lfs_size_t rdiff = hdiff + rsize; + + while (true) { + // and alt-pointer + hdiff = 0; + err = lfs_bd_read(lfs, + &lfs->pcache, &lfs->rcache, sizeof(header), + block, roff+rdiff, &header, sizeof(header)); + if (err) { + return err; + } + + lfs_tag_t ralt; + res = lfs_fromleb128_(&ralt, &header[0]); + if (res < 0) { + return res; + } + hdiff += res; + + lfs_tag_t nroff; + res = lfs_fromleb128_(&nroff, &header[hdiff]); + if (res < 0) { + return res; + } + hdiff += res; + rdiff += hdiff; + + // iterate through bits + for (; i < 28; i++) { + if ((tag ^ rtag) & (1 << (28-i))) { + // different bit, new alt-pointer + hdiff = 0; + hdiff += lfs_toleb128_( + &header[hdiff], LFS_MKALT_(28-i)); + hdiff += lfs_toleb128_( + &header[hdiff], roff); + printf("new alt 0x%08x 0x%08x\n", LFS_MKALT_(28-i), roff); + err = lfs_bd_prog_(lfs, + &lfs->pcache, &lfs->rcache, limit, crc, + block, off+diff, &header, hdiff); + if (err) { + return err; + } + diff += hdiff; + + // follow alt-pointer? + if (lfs_tag_isalt_(ralt) && + lfs_tag_alt_(ralt) == 28-i) { + roff = nroff; + goto next_tag; + } else { + roff = 0; + goto next_tag; + } + } else { + // same bit, copy alt-pointer + if (lfs_tag_isalt_(ralt) && + lfs_tag_alt_(ralt) == 28-i) { + printf("copy alt 0x%08x 0x%08x\n", LFS_MKALT_(28-i), roff); + err = lfs_bd_prog_(lfs, + &lfs->pcache, &lfs->rcache, limit, crc, + block, off+diff, &header, hdiff); + if (err) { + return err; + } + diff += hdiff; + } + + if (lfs_tag_isalt_(ralt) && + lfs_tag_alt_(ralt) > 28-i) { + // need new alt-pointer + goto next_alt; + } + } + } +next_alt:; + } +next_tag:; + } + + *proff = off; + } + + return diff; +} + +//int lfs_dir_commitattr_(lfs_t *lfs, struct lfs_commit *commit, +// lfs_tag_t tag, const void *buffer, lfs_size_t size) { +// bool fromdisk = 0x80000000; +// tag &= 0x7fffffff; +// +// // write out tag+size +// lfs_ssize_t res = lfs_bd_progleb128_(lfs, +// &lfs->pcache, &lfs->rcache, commit->end, &commit->crc, +// commit->block, commit->off, tag); +// if (res < 0) { +// return res; +// } +// commit->off += res; +// +// res = lfs_bd_progleb128_(lfs, +// &lfs->pcache, &lfs->rcache, commit->end, &commit->crc, +// commit->block, commit->off, size); +// if (res < 0) { +// return res; +// } +// commit->off += res; +// +// if (!fromdisk) { +// // from memory +// int err = lfs_bd_prog_(lfs, +// &lfs->pcache, &lfs->rcache, commit->end, &commit->crc, +// commit->block, commit->off, buffer, size); +// if (err) { +// return err; +// } +// commit->off += size; +// } else { +// // from disk +// const struct lfs_diskoff *disk = buffer; +// for (lfs_off_t i = 0; i < size; i++) { +// // rely on caching to make this efficient +// uint8_t dat; +// int err = lfs_bd_read(lfs, +// NULL, &lfs->rcache, size-i, +// disk->block, disk->off+i, &dat, 1); +// if (err) { +// return err; +// } +// +// err = lfs_bd_prog_(lfs, +// &lfs->pcache, &lfs->rcache, commit->end, &commit->crc, +// commit->block, commit->off+i, &dat, 1); +// if (err) { +// return err; +// } +// } +// commit->off += size; +// } +// +// return 0; +//} + +// TODO better arg order? +static lfs_ssize_t lfs_dir_commitattrs_(lfs_t *lfs, lfs_off_t *roff, + lfs_off_t limit, uint32_t *crc, lfs_block_t block, lfs_off_t off, + const struct lfs_mattr_ *attrs, lfs_size_t attr_count) { + lfs_size_t diff = 0; + + // iterate over tags + // TODO handle internal "from" tags + for (lfs_size_t i = 0; i < attr_count; i++) { + lfs_ssize_t res = lfs_dir_commitattr_(lfs, roff, + limit, crc, block, off+diff, + attrs[i].tag, attrs[i].buffer, attrs[i].size); + if (res < 0) { + return res; + } + diff += res; + } + + return diff; +} + +static int lfs_dir_commitfinalize_(lfs_t *lfs, lfs_mdir__t *mdir, + lfs_off_t *roff, lfs_off_t limit, uint32_t crc, + lfs_block_t block, lfs_off_t off) { + // align to program units + // + // this gets a bit complex as we have two types of commits: + // - commit with estate to check the next prog (middle of block) + // - commit without estate (end of block) + // + // sizeof(nprogcrc tag) = 6+log128(prog_size) + // sizeof(crc tag) = 5+log128(prog_size) + uint8_t estate[2*sizeof(uint32_t)]; + lfs_size_t ediff = lfs_toleb128_(&estate[0], lfs->cfg->prog_size); + lfs_size_t csize = lfs->cfg->block_size - limit; + lfs_off_t eoff = lfs_alignup( + lfs_min(off + 6+ediff + csize, lfs->cfg->block_size), + lfs->cfg->prog_size); + + bool eperturb = false; + // space for estate? + if (eoff <= lfs->cfg->block_size - lfs->cfg->prog_size) { + // first we check that next byte to see if we need to + // perturb our directory's next commit to avoid issues + // where the data we want to write matches the erase-state + uint8_t ebyte; + int err = lfs_bd_read(lfs, + NULL, &lfs->rcache, lfs->cfg->prog_size, + block, eoff, &ebyte, sizeof(ebyte)); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + eperturb = ~(ebyte & 0x1); + + // find expected erase-state + uint32_t ecrc = 0xffffffff; + err = lfs_bd_crc(lfs, + NULL, &lfs->rcache, lfs->cfg->prog_size, + block, eoff, lfs->cfg->prog_size, &ecrc); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + + lfs_tole32_(&estate[ediff], ecrc); + ediff += sizeof(uint32_t); + lfs_ssize_t res = lfs_dir_commitattr_(lfs, + roff, limit, &crc, block, off, + LFS_MKTAG_(LFS_T_CRC, LFS_S_NPROG, 0), estate, ediff); + if (res < 0) { + return res; + } + off += res; + } + + // build CRC tag, don't go through commitattr because we don't want + // to CRC padding + // TODO wait a sec, cyclic reference because of leb-size? + uint8_t cstate[9]; + cstate[0] = (uint8_t)LFS_MKTAG_(LFS_T_CRC, 0, 0); + + // manually write prog_size leb128 to avoid issues with + // off-by-1 padding + lfs_size_t padding = eoff - (off+csize-4); + for (lfs_size_t i = 1; i < csize-4-1; i++) { + cstate[i] = 0x80 | (padding & 0x7f); + padding >>= 7; + } + cstate[csize-4-1] = padding; + LFS_ASSERT(padding <= 0x7f); + + crc = lfs_crc(crc, &cstate[0], csize-4); + lfs_tole32_(&cstate[csize-4], crc); + + // TODO ugh, need to signal no validate without signalling crc... + // should use different API? separate lfs_bd_progcrc_? + int err = lfs_bd_prog_(lfs, &lfs->pcache, &lfs->rcache, + lfs->cfg->block_size, &(uint32_t){0}, block, off, + &cstate, csize); + if (err) { + return err; + } + off += csize; + + // flush buffers + err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); + if (err) { + return err; + } + + // successful commit, check checksums to make sure + uint32_t dcrc = 0xffffffff; + lfs_bd_crc(lfs, + NULL, &lfs->rcache, off-mdir->eoff, + block, mdir->eoff, off-mdir->eoff-sizeof(uint32_t), &dcrc); + if (err) { + return err; + } + + // check against known good CRC to avoid finding an + // unrelated CRC on a bad block + if (dcrc != crc) { + return LFS_ERR_CORRUPT; + } + + // make sure to check the CRC was written correctly as well + err = lfs_bd_crc(lfs, + NULL, &lfs->rcache, sizeof(uint32_t), + block, off-sizeof(uint32_t), sizeof(uint32_t), &crc); + if (err) { + return err; + } + + if (crc != 0) { + return LFS_ERR_CORRUPT; + } + + // update our metadata-pair struct + mdir->roff = *roff; + mdir->eoff = eoff; + mdir->eperturb = eperturb; + + return 0; +} + + +int lfs_dir_compact_(lfs_t *lfs, lfs_mdir__t *mdir, + const struct lfs_mattr_ *attrs, lfs_size_t attr_count) { + // increment revision count + mdir->rev += 1; + + // begin loop to commit compaction to blocks until a compact sticks + while (true) { + { + // setup commit state + lfs_block_t block = mdir->pair[1]; + lfs_off_t off = 0; + // need 5 bytes + leb128 of padding for CRC tag on end-of-commit, + // we use worst-case padding (prog_size) to avoid off-by-1 issues + // with leb128 encoding + lfs_off_t limit = lfs->cfg->block_size + - (5 + ((lfs_npw2(lfs->cfg->prog_size)+7-1)/7)); + lfs_off_t roff = mdir->roff; + uint32_t crc = 0xffffffff; + + // erase block to write to + int err = lfs_bd_erase(lfs, mdir->pair[1]); + if (err) { + if (err == LFS_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + // write out header + err = lfs_bd_prog_(lfs, + &lfs->pcache, &lfs->rcache, limit, &crc, + block, off, + &(uint32_t){lfs_tole32(mdir->rev)}, sizeof(uint32_t)); + if (err) { + if (err == LFS_ERR_CORRUPT) { + goto relocate; + } + return err; + } + off += sizeof(uint32_t); + + // write out tags + // TODO argument order nonsense? this order seems wrong + int res = lfs_dir_commitattrs_(lfs, + &roff, limit, &crc, block, off, + attrs, attr_count); + if (res < 0) { + if (res == LFS_ERR_CORRUPT) { + goto relocate; + } + return res; + } + off += res; + + // finalize commit + res = lfs_dir_commitfinalize_(lfs, mdir, + &roff, limit, crc, block, off); + if (res < 0) { + if (res == LFS_ERR_CORRUPT) { + goto relocate; + } + return res; + } + + break; + } + +relocate: + LFS_ASSERT(false); // TODO + } + + return 0; +} + + + + +//static int lfs_dir_rawcommit_(lfs_t *lfs, lfs_mdir_t *mdir, +// const struct lfs_mattr_ *attrs, int attrcount) { +// if (mdir->eoff != 0xffffffff) { +// +// } +// + +// // TODO can use dir->noff for this? +// if (dir->erased) { +// // try to commit +// struct lfs_commit commit = { +// .block = dir->pair[0], +// .off = dir->off, +// .ptag = dir->etag, +// .crc = 0xffffffff, +// +// .begin = dir->off, +// .end = lfs->cfg->block_size - 8, +// }; +// +// // traverse attrs that need to be written out +// lfs_pair_tole32(dir->tail); +// 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); +// if (err) { +// if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { +// goto compact; +// } +// *dir = olddir; +// return err; +// } +// +// // commit any global diffs if we have any +// lfs_gstate_t delta = {0}; +// lfs_gstate_xor(&delta, &lfs->gstate); +// lfs_gstate_xor(&delta, &lfs->gdisk); +// lfs_gstate_xor(&delta, &lfs->gdelta); +// delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff); +// if (!lfs_gstate_iszero(&delta)) { +// err = lfs_dir_getgstate(lfs, dir, &delta); +// if (err) { +// *dir = olddir; +// return err; +// } +// +// lfs_gstate_tole32(&delta); +// err = lfs_dir_commitattr(lfs, &commit, +// LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, +// sizeof(delta)), &delta); +// if (err) { +// if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { +// goto compact; +// } +// *dir = olddir; +// return err; +// } +// } +// +// // finalize commit with the crc +// err = lfs_dir_commitcrc(lfs, &commit); +// if (err) { +// if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { +// goto compact; +// } +// *dir = olddir; +// return err; +// } +// +// // successful commit, update dir +// LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); +// dir->off = commit.off; +// dir->etag = commit.ptag; +// // and update gstate +// lfs->gdisk = lfs->gstate; +// lfs->gdelta = (lfs_gstate_t){0}; +// } else { + + +//} + + /// Top level directory operations /// #ifndef LFS_READONLY diff --git a/lfs.h b/lfs.h index 42a21b90..c48015c3 100644 --- a/lfs.h +++ b/lfs.h @@ -85,6 +85,7 @@ enum lfs_error { LFS_ERR_NOMEM = -12, // No more memory available LFS_ERR_NOATTR = -61, // No data/attr available LFS_ERR_NAMETOOLONG = -36, // File name too long + LFS_ERR_OVERFLOW = -75, // Value too large for defined data type }; // File types @@ -120,6 +121,13 @@ enum lfs_type { LFS_FROM_NOOP = 0x000, LFS_FROM_MOVE = 0x101, LFS_FROM_USERATTRS = 0x102, + + // internally used tag-types + LFS_T_CRC = 0xc, + LFS_T_USERATTR = 0x2, + + // internally used tag-subtypes + LFS_S_NPROG = 0x01, }; // File open flags @@ -321,6 +329,19 @@ typedef struct lfs_cache { uint8_t *buffer; } lfs_cache_t; +typedef struct lfs_mdir_ { + lfs_block_t pair[2]; + uint32_t rev; + lfs_off_t roff; + lfs_off_t eoff; + uint16_t count; + + //bool erased; + bool split; + bool eperturb; + lfs_block_t tail[2]; +} lfs_mdir__t; + typedef struct lfs_mdir { lfs_block_t pair[2]; uint32_t rev; diff --git a/tests/test_mdirs.toml b/tests/test_mdirs.toml new file mode 100644 index 00000000..ad89d05c --- /dev/null +++ b/tests/test_mdirs.toml @@ -0,0 +1,38 @@ +# Test internal operations of metadata-pairs + +[[case]] +in = "lfs.c" +code = ''' + lfs_init(&lfs, &cfg) => 0; + lfs_mdir__t mdir = { + .pair = {0, 1}, + .rev = 0, + .roff = 0, + .eoff = 0, + .count = 0, + .split = false, + .eperturb = false, + .tail = {0xffffffff, 0xffffffff}, + }; + + lfs_dir_compact_(&lfs, &mdir, LFS_MKATTRS_( + {LFS_MKTAG_(LFS_T_USERATTR, 0, 0), "0000", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 01, 0), "0001", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 2, 0), "0002", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 3, 0), "0003", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 4, 0), "0004", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 5, 0), "0005", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 6, 0), "0006", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 7, 0), "0007", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 8, 0), "0008", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 9, 0), "0009", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 10, 0), "0010", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 11, 0), "0011", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 12, 0), "0012", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 13, 0), "0013", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 14, 0), "0014", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 15, 0), "0015", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 0xaa, 0xaaaa), "0015", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 0xcc, 0xcccc), "0015", 4}, + {LFS_MKTAG_(LFS_T_USERATTR, 0xff, 0xffff), "0016", 4})) => 0; +'''