|
|
@@ -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);
|
|
|
+ // 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 (!lfs_pair_isnull(dir->tail)) {
|
|
|
- // commit tail, which may be new after last size check
|
|
|
- lfs_pair_tole32(dir->tail);
|
|
|
+ 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;
|
|
|
}
|
|
|
|