WIP initial commit of radix-based metadata-blocks
Currently commit-only
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
'''
|
||||
Reference in New Issue
Block a user