Modified global state format to work with new tag format

The main difference here is a change from encoding "hasorphans" and
"hasmove" bits in the tag itself. This worked with the old format, but
in the new format the space these bits take up must be consistent for
each tag type. The tradeoff is that the new tag format allows for up to
256 different global states which may be useful in the future (for
example, a global free list).

The new format encodes this info in the data blob, using an additional
word of storage. This word is actually formatted the same as though it
was a tag, which simplified internal handling and may allow other tag
types in the future.

Format for global state:
[----                          96 bits                         ----]
[1|- 11 -|- 10 -|- 10 -|---                 64                  ---]
 ^    ^      ^      ^                        ^- move dir pair
 |    |      |      \-------------------------- unused, must be 0s
 |    |      \--------------------------------- move id
 |    \---------------------------------------- type, 0xfff for move
 \--------------------------------------------- has orphans

This also included another iteration over globals (renamed to gstate)
with some simplifications to how globals are handled.
This commit is contained in:
Christopher Haster
2019-01-04 17:23:36 -06:00
parent b989b4a89f
commit 66d751544d
3 changed files with 209 additions and 240 deletions

397
lfs.c
View File

@@ -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;
}