diff --git a/lfs.c b/lfs.c index 3d3aea0..d0c89f9 100644 --- a/lfs.c +++ b/lfs.c @@ -331,56 +331,48 @@ struct lfs_diskoff { lfs_off_t off; }; -// operations on set of globals -static inline void lfs_global_xor(struct lfs_globals *a, - const struct lfs_globals *b) { - uint32_t *a32 = (uint32_t *)a; - const uint32_t *b32 = (const uint32_t *)b; - for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) { - a32[i] ^= b32[i]; +// operations on global state +static inline void lfs_gstate_xor(struct lfs_gstate *a, + const struct lfs_gstate *b) { + for (int i = 0; i < 3; i++) { + ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; } } -static inline bool lfs_global_iszero(const struct lfs_globals *a) { - const uint32_t *a32 = (const uint32_t *)a; - for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) { - if (a32[i] != 0) { +static inline bool lfs_gstate_iszero(const struct lfs_gstate *a) { + for (int i = 0; i < 3; i++) { + if (((uint32_t*)a)[i] != 0) { return false; } } return true; } -static inline void lfs_global_zero(struct lfs_globals *a) { - lfs_global_xor(a, a); +static inline bool lfs_gstate_hasorphans(const struct lfs_gstate *a) { + return lfs_tag_size(a->tag); } -static inline void lfs_global_fromle32(struct lfs_globals *a) { - lfs_pair_fromle32(a->pair); - a->id = lfs_fromle16(a->id); +static inline uint8_t lfs_gstate_getorphans(const struct lfs_gstate *a) { + return lfs_tag_size(a->tag); } -static inline void lfs_global_tole32(struct lfs_globals *a) { - lfs_pair_tole32(a->pair); - a->id = lfs_tole16(a->id); +static inline bool lfs_gstate_hasmove(const struct lfs_gstate *a) { + return lfs_tag_type1(a->tag); } -static inline void lfs_global_move(lfs_t *lfs, - bool hasmove, const lfs_block_t pair[2], uint16_t id) { - lfs_global_fromle32(&lfs->locals); - lfs_global_xor(&lfs->locals, &lfs->globals); - lfs->globals.hasmove = hasmove; - lfs->globals.pair[0] = pair[0]; - lfs->globals.pair[1] = pair[1]; - lfs->globals.id = id; - lfs_global_xor(&lfs->locals, &lfs->globals); - lfs_global_tole32(&lfs->locals); +static inline bool lfs_gstate_hasmovehere(const struct lfs_gstate *a, + const lfs_block_t *pair) { + return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0; } -static inline void lfs_global_orphans(lfs_t *lfs, int8_t orphans) { - lfs->locals.orphans ^= (lfs->globals.orphans == 0); - lfs->globals.orphans += orphans; - lfs->locals.orphans ^= (lfs->globals.orphans == 0); +static inline void lfs_gstate_fromle32(struct lfs_gstate *a) { + for (int i = 0; i < 3; i++) { + ((uint32_t*)a)[i] = lfs_fromle32(((uint32_t*)a)[i]); + } +} + +static inline void lfs_gstate_tole32(struct lfs_gstate *a) { + lfs_gstate_fromle32(a); } // other endianness operations @@ -427,6 +419,9 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], lfs_mdir_t *parent); static int lfs_fs_relocate(lfs_t *lfs, const lfs_block_t oldpair[2], lfs_block_t newpair[2]); +static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); +static void lfs_fs_prepmove(lfs_t *lfs, + uint16_t id, const lfs_block_t pair[2]); static int lfs_fs_forceconsistency(lfs_t *lfs); static int lfs_deinit(lfs_t *lfs); @@ -501,9 +496,8 @@ static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir, lfs_tag_t ntag = dir->etag; 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) { + if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair) && + lfs_tag_id(gtag) <= lfs_tag_id(lfs->gstate.tag)) { // synthetic moves gdiff -= LFS_MKTAG(0, 1, 0); } @@ -582,13 +576,12 @@ static int lfs_dir_traverse_filter(void *p, 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, + const struct lfs_mattr *attrs, int attrcount, bool hasseenmove, 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) { - // 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) { + // iterate over directory and attrs + while (true) { lfs_tag_t tag; const void *buffer; struct lfs_diskoff disk; @@ -606,7 +599,7 @@ static int lfs_dir_traverse(lfs_t *lfs, disk.off = off+sizeof(lfs_tag_t); buffer = &disk; ptag = tag; - } else { + } else if (attrcount > 0) { const struct lfs_mattr *a = attrs; for (int j = 0; j < attrcount-1; j++) { a = a->next; @@ -615,6 +608,15 @@ static int lfs_dir_traverse(lfs_t *lfs, tag = a->tag; buffer = a->buffer; attrcount -= 1; + } else if (!hasseenmove && + lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { + // Wait, we have pending move? Handle this here (we need to + // or else we risk letting moves fall out of date) + tag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0); + buffer = NULL; + hasseenmove = true; + } else { + return 0; } lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); @@ -627,7 +629,7 @@ static int lfs_dir_traverse(lfs_t *lfs, if (lfs_tag_id(tmask) != 0) { // scan for duplicates and update tag based on creates/deletes int filter = lfs_dir_traverse(lfs, - dir, off, ptag, attrs, attrcount, + dir, off, ptag, attrs, attrcount, hasseenmove, 0, 0, 0, 0, 0, lfs_dir_traverse_filter, &tag); if (filter < 0) { @@ -649,7 +651,7 @@ static int lfs_dir_traverse(lfs_t *lfs, uint16_t fromid = lfs_tag_size(tag); uint16_t toid = lfs_tag_id(tag); int err = lfs_dir_traverse(lfs, - buffer, 0, 0xffffffff, NULL, 0, + buffer, 0, 0xffffffff, NULL, 0, true, LFS_MKTAG(0x600, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), fromid, fromid+1, toid-fromid+diff, @@ -672,8 +674,6 @@ static int lfs_dir_traverse(lfs_t *lfs, } } } - - return 0; } static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, @@ -824,10 +824,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, } } 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)); + dir->pair[0], off+sizeof(tag), &temptail, 8); if (err) { if (err == LFS_ERR_CORRUPT) { dir->erased = false; @@ -863,12 +863,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, // consider what we have good enough if (dir->off > 0) { // synthetic move - if (lfs->globals.hasmove && - lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) { - if (lfs->globals.id == lfs_tag_id(besttag)) { + if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair)) { + if (lfs_tag_id(lfs->gstate.tag) == lfs_tag_id(besttag)) { besttag |= 0x80000000; } else if (besttag != -1 && - lfs->globals.id < lfs_tag_id(besttag)) { + lfs_tag_id(lfs->gstate.tag) < lfs_tag_id(besttag)) { besttag -= LFS_MKTAG(0, 1, 0); } } @@ -904,20 +903,18 @@ static int lfs_dir_fetch(lfs_t *lfs, 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, LFS_MKTAG(0x700, 0, 0), - LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals); +static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir, + struct lfs_gstate *gstate) { + struct lfs_gstate temp; + lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0), + LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp); if (res < 0 && res != LFS_ERR_NOENT) { return res; } if (res != LFS_ERR_NOENT) { - 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); + // xor together to find resulting gstate + lfs_gstate_xor(gstate, &temp); } return 0; @@ -1158,13 +1155,6 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit, return 0; } -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, - 0x3ff, 10), globals); -} - static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { // align to program units lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t), @@ -1181,8 +1171,8 @@ 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, - 0x3ff, off - (commit->off+sizeof(lfs_tag_t))); + tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, + off - (commit->off+sizeof(lfs_tag_t))); // write out crc uint32_t footer[2]; @@ -1267,23 +1257,24 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { return 0; } -static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, const lfs_mdir_t *tail) { - // steal tail - dir->tail[0] = tail->tail[0]; - dir->tail[1] = tail->tail[1]; - dir->split = tail->split; - +static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { // steal state - int err = lfs_dir_getglobals(lfs, tail, &lfs->locals); + int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta); if (err) { return err; } - // update pred's tail - return lfs_dir_commit(lfs, dir, - LFS_MKATTR(LFS_TYPE_TAIL + dir->split, - 0x3ff, dir->tail, sizeof(dir->tail), + // steal tail + lfs_pair_tole32(tail->tail); + err = lfs_dir_commit(lfs, dir, + LFS_MKATTR(LFS_TYPE_TAIL + tail->split, 0x3ff, tail->tail, 8, NULL)); + lfs_pair_fromle32(tail->tail); + if (err) { + return err; + } + + return 0; } static int lfs_dir_split(lfs_t *lfs, @@ -1341,7 +1332,6 @@ static int lfs_dir_compact(lfs_t *lfs, 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]}; - struct lfs_globals globals, locals; bool relocated = false; bool exhausted = false; @@ -1349,7 +1339,7 @@ static int lfs_dir_compact(lfs_t *lfs, // find size lfs_size_t size = 0; int err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, + source, 0, 0xffffffff, attrs, attrcount, false, LFS_MKTAG(0x400, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_NAME, 0, 0), begin, end, -begin, @@ -1358,10 +1348,10 @@ static int lfs_dir_compact(lfs_t *lfs, return err; } - // space is complicated, we need room for tail, crc, globals, + // space is complicated, we need room for tail, crc, gstate, // cleanup delete, and we cap at half a block to give room // for metadata updates - if (size <= lfs_min(lfs->cfg->block_size - 38, + if (size <= lfs_min(lfs->cfg->block_size - 36, lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size))) { break; } @@ -1376,7 +1366,7 @@ static int lfs_dir_compact(lfs_t *lfs, // 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 // only option is to error - if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 38) { + if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 36) { break; } return err; @@ -1424,9 +1414,7 @@ static int lfs_dir_compact(lfs_t *lfs, if (true) { // There's nothing special about our global delta, so feed it into // our local global delta - globals = lfs->globals; - locals = lfs->locals; - int err = lfs_dir_getglobals(lfs, dir, &locals); + int err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta); if (err) { return err; } @@ -1465,7 +1453,7 @@ static int lfs_dir_compact(lfs_t *lfs, // traverse the directory, this time writing out all unique tags err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, + source, 0, 0xffffffff, attrs, attrcount, false, LFS_MKTAG(0x400, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_NAME, 0, 0), begin, end, -begin, @@ -1478,27 +1466,32 @@ static int lfs_dir_compact(lfs_t *lfs, return err; } - if (!relocated && !lfs_global_iszero(&locals)) { - // commit any globals, unless we're relocating, - // in which case our parent will steal our globals - err = lfs_dir_commitglobals(lfs, &commit, &locals); + // commit tail, which may be new after last size check + if (!lfs_pair_isnull(dir->tail)) { + lfs_pair_tole32(dir->tail); + err = lfs_dir_commitattr(lfs, &commit, + LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), + dir->tail); + lfs_pair_fromle32(dir->tail); if (err) { if (err == LFS_ERR_CORRUPT) { goto relocate; } return err; } - - lfs_global_zero(&locals); } - if (!lfs_pair_isnull(dir->tail)) { - // commit tail, which may be new after last size check - lfs_pair_tole32(dir->tail); + // need to update gstate now that we've acknowledged moves + if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { + lfs_fs_prepmove(lfs, 0x3ff, NULL); + } + + if (!relocated && !lfs_gstate_iszero(&lfs->gdelta)) { + // commit any globals, unless we're relocating, + // in which case our parent will steal our globals err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_TAIL + dir->split, - 0x3ff, sizeof(dir->tail)), dir->tail); - lfs_pair_fromle32(dir->tail); + LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, + sizeof(lfs->gdelta)), &lfs->gdelta); if (err) { if (err == LFS_ERR_CORRUPT) { goto relocate; @@ -1547,11 +1540,10 @@ relocate: continue; } - // successful commit, update globals - lfs->globals = globals; - lfs->locals = locals; - - if (relocated) { + if (!relocated) { + lfs->gstate = lfs->gpending; + lfs->gdelta = (struct lfs_gstate){0}; + } else { // update references if we relocated LFS_DEBUG("Relocating %"PRIu32" %"PRIu32" to %"PRIu32" %"PRIu32, oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); @@ -1566,47 +1558,35 @@ relocate: static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, const struct lfs_mattr *attrs) { - // check for globals work - struct lfs_mattr cancelattr; - struct lfs_globals cancels; - lfs_global_zero(&cancels); - if (lfs->globals.hasmove && - lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) { - // Wait, we have the move? Just cancel this out here - // We need to, or else the move can become outdated - cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.id, 0); - cancelattr.next = attrs; - attrs = &cancelattr; - - cancels.hasmove = lfs->globals.hasmove; - cancels.pair[0] = lfs->globals.pair[0]; - cancels.pair[1] = lfs->globals.pair[1]; - cancels.id = lfs->globals.id; - lfs_global_fromle32(&lfs->locals); - lfs_global_xor(&lfs->locals, &cancels); - lfs_global_tole32(&lfs->locals); - } - - struct lfs_globals globals = lfs->globals; - struct lfs_globals locals = lfs->locals; - - // calculate new directory size + // calculate changes to the directory lfs_tag_t deletetag = 0xffffffff; lfs_tag_t createtag = 0xffffffff; int attrcount = 0; for (const struct lfs_mattr *a = attrs; a; a = a->next) { if (lfs_tag_type3(a->tag) == LFS_TYPE_CREATE) { - dir->count += 1; createtag = a->tag; + dir->count += 1; } else if (lfs_tag_type3(a->tag) == LFS_TYPE_DELETE) { + deletetag = a->tag; LFS_ASSERT(dir->count > 0); dir->count -= 1; - deletetag = a->tag; + } else if (lfs_tag_type1(a->tag) == LFS_TYPE_TAIL) { + dir->tail[0] = ((lfs_block_t*)a->buffer)[0]; + dir->tail[1] = ((lfs_block_t*)a->buffer)[1]; + dir->split = (lfs_tag_chunk(a->tag) & 1); + lfs_pair_fromle32(dir->tail); } attrcount += 1; } + // do we have a pending move? + if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { + deletetag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0); + LFS_ASSERT(dir->count > 0); + dir->count -= 1; + } + // should we actually drop the directory block? if (lfs_tag_isvalid(deletetag) && dir->count == 0) { lfs_mdir_t pdir; @@ -1635,7 +1615,7 @@ 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_traverse(lfs, - dir, dir->off, dir->etag, attrs, attrcount, + dir, dir->off, dir->etag, attrs, attrcount, false, 0, 0, 0, 0, 0, lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ lfs, &commit}); @@ -1647,22 +1627,27 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, return err; } + // need to update gstate now that we've acknowledged moves + if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { + lfs_fs_prepmove(lfs, 0x3ff, NULL); + } + // commit any global diffs if we have any - if (!lfs_global_iszero(&locals)) { - err = lfs_dir_getglobals(lfs, dir, &locals); + if (!lfs_gstate_iszero(&lfs->gdelta)) { + err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta); if (err) { return err; } - err = lfs_dir_commitglobals(lfs, &commit, &locals); + err = lfs_dir_commitattr(lfs, &commit, + LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, + sizeof(lfs->gdelta)), &lfs->gdelta); if (err) { if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { goto compact; } return err; } - - lfs_global_zero(&locals); } // finalize commit with the crc @@ -1677,9 +1662,9 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, // successful commit, update dir dir->off = commit.off; dir->etag = commit.ptag; - // successful commit, update globals - lfs->globals = globals; - lfs->locals = locals; + // successful commit, update gstate + lfs->gstate = lfs->gpending; + lfs->gdelta = (struct lfs_gstate){0}; } else { compact: // fall back to compaction @@ -1692,9 +1677,6 @@ compact: } } - // update globals that are affected - lfs_global_xor(&lfs->globals, &cancels); - // update any directories that are affected lfs_mdir_t copy = *dir; @@ -1772,31 +1754,28 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { } // setup dir - dir.tail[0] = pred.tail[0]; - dir.tail[1] = pred.tail[1]; - err = lfs_dir_commit(lfs, &dir, NULL); + lfs_pair_tole32(pred.tail); + err = lfs_dir_commit(lfs, &dir, + LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pred.tail, 8, + NULL)); + lfs_pair_fromle32(pred.tail); if (err) { return err; } // current block end of list? - if (!cwd.split) { - // update atomically - cwd.tail[0] = dir.pair[0]; - cwd.tail[1] = dir.pair[1]; - } else { + if (cwd.split) { // update tails, this creates a desync - pred.tail[0] = dir.pair[0]; - pred.tail[1] = dir.pair[1]; - lfs_global_orphans(lfs, +1); + lfs_fs_preporphans(lfs, +1); + lfs_pair_tole32(dir.pair); err = lfs_dir_commit(lfs, &pred, - LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, - pred.tail, sizeof(pred.tail), + LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8, NULL)); + lfs_pair_fromle32(dir.pair); if (err) { return err; } - lfs_global_orphans(lfs, -1); + lfs_fs_preporphans(lfs, -1); } // now insert into our parent block @@ -1806,8 +1785,8 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { LFS_MKATTR(LFS_TYPE_DIR, id, path, nlen, LFS_MKATTR(LFS_TYPE_CREATE, id, NULL, 0, (!cwd.split) - ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, - cwd.tail, sizeof(cwd.tail), NULL) + ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8, + NULL) : NULL)))); lfs_pair_fromle32(dir.pair); if (err) { @@ -2867,7 +2846,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { } // mark fs as orphaned - lfs_global_orphans(lfs, +1); + lfs_fs_preporphans(lfs, +1); } // delete the entry @@ -2880,7 +2859,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { // fix orphan - lfs_global_orphans(lfs, -1); + lfs_fs_preporphans(lfs, -1); err = lfs_fs_pred(lfs, dir.pair, &cwd); if (err) { @@ -2948,7 +2927,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { } // mark fs as orphaned - lfs_global_orphans(lfs, +1); + lfs_fs_preporphans(lfs, +1); } // create move to fix later @@ -2960,7 +2939,8 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { // is a bit messy newoldtagid += 1; } - lfs_global_move(lfs, true, oldcwd.pair, newoldtagid); + + lfs_fs_prepmove(lfs, newoldtagid, oldcwd.pair); // move over all attributes err = lfs_dir_commit(lfs, &newcwd, @@ -2985,7 +2965,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { // fix orphan - lfs_global_orphans(lfs, -1); + lfs_fs_preporphans(lfs, -1); err = lfs_fs_pred(lfs, prevdir.pair, &newcwd); if (err) { @@ -3155,8 +3135,9 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->root[1] = 0xffffffff; lfs->mlist = NULL; lfs->seed = 0; - lfs_global_zero(&lfs->globals); - lfs_global_zero(&lfs->locals); + lfs->gstate = (struct lfs_gstate){0}; + lfs->gpending = (struct lfs_gstate){0}; + lfs->gdelta = (struct lfs_gstate){0}; return 0; @@ -3334,8 +3315,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { } } - // has globals? - err = lfs_dir_getglobals(lfs, &dir, &lfs->locals); + // has gstate? + err = lfs_dir_getgstate(lfs, &dir, &lfs->gpending); if (err) { return err; } @@ -3347,13 +3328,14 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { goto cleanup; } - // update littlefs with globals - lfs_global_fromle32(&lfs->locals); - lfs_global_xor(&lfs->globals, &lfs->locals); - lfs_global_zero(&lfs->locals); - if (lfs->globals.hasmove) { + // update littlefs with gstate + lfs_gstate_fromle32(&lfs->gpending); + lfs->gstate = lfs->gpending; + if (lfs_gstate_hasmove(&lfs->gstate)) { LFS_DEBUG("Found move %"PRIu32" %"PRIu32" %"PRIu32, - lfs->globals.pair[0], lfs->globals.pair[1], lfs->globals.id); + lfs->gstate.pair[0], + lfs->gstate.pair[1], + lfs_tag_id(lfs->gstate.tag)); } // setup free lookahead @@ -3531,7 +3513,7 @@ static int lfs_fs_relocate(lfs_t *lfs, if (tag != LFS_ERR_NOENT) { // update disk, this creates a desync - lfs_global_orphans(lfs, +1); + lfs_fs_preporphans(lfs, +1); lfs_pair_tole32(newpair); int err = lfs_dir_commit(lfs, &parent, @@ -3542,7 +3524,7 @@ static int lfs_fs_relocate(lfs_t *lfs, } // next step, clean up orphans - lfs_global_orphans(lfs, -1); + lfs_fs_preporphans(lfs, -1); } // find pred @@ -3554,12 +3536,11 @@ static int lfs_fs_relocate(lfs_t *lfs, // if we can't find dir, it must be new if (err != LFS_ERR_NOENT) { // replace bad pair, either we clean up desync, or no desync occured - parent.tail[0] = newpair[0]; - parent.tail[1] = newpair[1]; + lfs_pair_tole32(newpair); err = lfs_dir_commit(lfs, &parent, - LFS_MKATTR(LFS_TYPE_TAIL + parent.split, - 0x3ff, parent.tail, sizeof(parent.tail), + LFS_MKATTR(LFS_TYPE_TAIL + parent.split, 0x3ff, newpair, 8, NULL)); + lfs_pair_fromle32(newpair); if (err) { return err; } @@ -3568,18 +3549,52 @@ static int lfs_fs_relocate(lfs_t *lfs, return 0; } +static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) { + lfs_gstate_fromle32(&lfs->gdelta); + lfs->gdelta.tag ^= lfs_gstate_hasorphans(&lfs->gpending); + + lfs->gpending.tag += orphans; + + lfs->gdelta.tag ^= lfs_gstate_hasorphans(&lfs->gpending); + lfs_gstate_tole32(&lfs->gdelta); +} + +static void lfs_fs_prepmove(lfs_t *lfs, + uint16_t id, const lfs_block_t pair[2]) { + lfs_gstate_fromle32(&lfs->gdelta); + lfs_gstate_xor(&lfs->gdelta, &lfs->gpending); + + if (id != 0x3ff) { + lfs->gpending.tag = LFS_MKTAG(LFS_TYPE_DELETE, id, + lfs_gstate_getorphans(&lfs->gpending)); + lfs->gpending.pair[0] = pair[0]; + lfs->gpending.pair[1] = pair[1]; + } else { + lfs->gpending.tag = LFS_MKTAG(0, 0, + lfs_gstate_getorphans(&lfs->gpending)); + lfs->gpending.pair[0] = 0; + lfs->gpending.pair[1] = 0; + } + + lfs_gstate_xor(&lfs->gdelta, &lfs->gpending); + lfs_gstate_tole32(&lfs->gdelta); +} + + static int lfs_fs_demove(lfs_t *lfs) { - if (!lfs->globals.hasmove) { + if (!lfs_gstate_hasmove(&lfs->gstate)) { return 0; } // Fix bad moves LFS_DEBUG("Fixing move %"PRIu32" %"PRIu32" %"PRIu32, - lfs->globals.pair[0], lfs->globals.pair[1], lfs->globals.id); + lfs->gstate.pair[0], + lfs->gstate.pair[1], + lfs_tag_id(lfs->gstate.tag)); // fetch and delete the moved entry lfs_mdir_t movedir; - int err = lfs_dir_fetch(lfs, &movedir, lfs->globals.pair); + int err = lfs_dir_fetch(lfs, &movedir, lfs->gstate.pair); if (err) { return err; } @@ -3594,7 +3609,7 @@ static int lfs_fs_demove(lfs_t *lfs) { } static int lfs_fs_deorphan(lfs_t *lfs) { - if (!lfs->globals.orphans) { + if (!lfs_gstate_hasorphans(&lfs->gstate)) { return 0; } @@ -3644,12 +3659,11 @@ static int lfs_fs_deorphan(lfs_t *lfs) { LFS_DEBUG("Fixing half-orphan %"PRIu32" %"PRIu32, pair[0], pair[1]); - pdir.tail[0] = pair[0]; - pdir.tail[1] = pair[1]; + lfs_pair_tole32(pair); err = lfs_dir_commit(lfs, &pdir, - LFS_MKATTR(LFS_TYPE_SOFTTAIL, - 0x3ff, pdir.tail, sizeof(pdir.tail), + LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pair, 8, NULL)); + lfs_pair_fromle32(pair); if (err) { return err; } @@ -3662,7 +3676,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) { } // mark orphans as fixed - lfs_global_orphans(lfs, -lfs->globals.orphans); + lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate)); + lfs->gstate = lfs->gpending; return 0; } diff --git a/lfs.h b/lfs.h index 05ba540..4dba2b7 100644 --- a/lfs.h +++ b/lfs.h @@ -381,12 +381,10 @@ typedef struct lfs { } *mlist; uint32_t seed; - struct lfs_globals { + struct lfs_gstate { + uint32_t tag; lfs_block_t pair[2]; - uint16_t id; - bool hasmove; - uint8_t orphans; - } globals, locals; + } gstate, gpending, gdelta; struct lfs_free { lfs_block_t off; diff --git a/lfs_util.h b/lfs_util.h index 6e2f002..1dc3b0f 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -164,28 +164,6 @@ static inline uint32_t lfs_tole32(uint32_t a) { return lfs_fromle32(a); } -// Convert between 16-bit little-endian and native order -static inline uint16_t lfs_fromle16(uint16_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) - return a; -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) - return __builtin_bswap16(a); -#else - return (((uint8_t*)&a)[0] << 0) | - (((uint8_t*)&a)[1] << 8); -#endif -} - -static inline uint16_t lfs_tole16(uint16_t a) { - return lfs_fromle16(a); -} - // Convert between 32-bit big-endian and native order static inline uint32_t lfs_frombe32(uint32_t a) { #if !defined(LFS_NO_INTRINSICS) && ( \ @@ -210,28 +188,6 @@ static inline uint32_t lfs_tobe32(uint32_t a) { return lfs_frombe32(a); } -// Convert between 16-bit big-endian and native order -static inline uint16_t lfs_frombe16(uint16_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) - return __builtin_bswap16(a); -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) - return a; -#else - return (((uint8_t*)&a)[0] << 8) | - (((uint8_t*)&a)[1] << 0); -#endif -} - -static inline uint16_t lfs_tobe16(uint16_t a) { - return lfs_frombe16(a); -} - // Calculate CRC-32 with polynomial = 0x04c11db7 uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);