mirror of
https://github.com/eledio-devices/thirdparty-littlefs.git
synced 2025-11-01 16:14:13 +01:00
WIP crazy new idea work in progress
passing non-reentrant tests already!
This commit is contained in:
259
lfs.c
259
lfs.c
@@ -241,6 +241,13 @@ static inline int lfs_pair_cmp(
|
|||||||
paira[0] == pairb[1] || paira[1] == pairb[0]);
|
paira[0] == pairb[1] || paira[1] == pairb[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int lfs_pair_realcmp(
|
||||||
|
const lfs_block_t paira[2],
|
||||||
|
const lfs_block_t pairb[2]) {
|
||||||
|
return !((paira[0] == pairb[0] && paira[1] == pairb[1]) ||
|
||||||
|
(paira[0] == pairb[1] && paira[1] == pairb[0]));
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool lfs_pair_sync(
|
static inline bool lfs_pair_sync(
|
||||||
const lfs_block_t paira[2],
|
const lfs_block_t paira[2],
|
||||||
const lfs_block_t pairb[2]) {
|
const lfs_block_t pairb[2]) {
|
||||||
@@ -423,6 +430,7 @@ int lfs_fs_traverseraw(lfs_t *lfs,
|
|||||||
int (*cb)(void *data, lfs_block_t block), void *data,
|
int (*cb)(void *data, lfs_block_t block), void *data,
|
||||||
bool includeorphans);
|
bool includeorphans);
|
||||||
static int lfs_fs_forceconsistency(lfs_t *lfs);
|
static int lfs_fs_forceconsistency(lfs_t *lfs);
|
||||||
|
static int lfs_fs_deorphan(lfs_t *lfs);
|
||||||
static int lfs_deinit(lfs_t *lfs);
|
static int lfs_deinit(lfs_t *lfs);
|
||||||
#ifdef LFS_MIGRATE
|
#ifdef LFS_MIGRATE
|
||||||
static int lfs1_traverse(lfs_t *lfs,
|
static int lfs1_traverse(lfs_t *lfs,
|
||||||
@@ -808,6 +816,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
// can't continue?
|
// can't continue?
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
@@ -820,9 +829,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
if (!lfs_tag_isvalid(tag)) {
|
if (!lfs_tag_isvalid(tag)) {
|
||||||
dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC &&
|
dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC &&
|
||||||
dir->off % lfs->cfg->prog_size == 0);
|
dir->off % lfs->cfg->prog_size == 0);
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
} else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
|
} else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,6 +848,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
@@ -845,6 +857,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
|
|
||||||
if (crc != dcrc) {
|
if (crc != dcrc) {
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -880,6 +893,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
@@ -913,6 +927,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -935,6 +950,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -948,6 +964,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (res == LFS_ERR_CORRUPT) {
|
if (res == LFS_ERR_CORRUPT) {
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -1419,6 +1436,7 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
|
|||||||
dir->branch[0] = LFS_BLOCK_NULL;
|
dir->branch[0] = LFS_BLOCK_NULL;
|
||||||
dir->branch[1] = LFS_BLOCK_NULL;
|
dir->branch[1] = LFS_BLOCK_NULL;
|
||||||
dir->erased = false;
|
dir->erased = false;
|
||||||
|
dir->first = true;
|
||||||
dir->split = false;
|
dir->split = false;
|
||||||
|
|
||||||
// don't write out yet, let caller take care of that
|
// don't write out yet, let caller take care of that
|
||||||
@@ -1442,6 +1460,7 @@ static int lfs_dir_droptail(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// steal tail's tail
|
// steal tail's tail
|
||||||
|
// TODO does this tail update cause problems
|
||||||
lfs_pair_tole32(tail->tail);
|
lfs_pair_tole32(tail->tail);
|
||||||
err = lfs_dir_commit(lfs, dir, LFS_MKATTRS(
|
err = lfs_dir_commit(lfs, dir, LFS_MKATTRS(
|
||||||
{LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail}));
|
{LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail}));
|
||||||
@@ -1863,6 +1882,7 @@ relocate:
|
|||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
// find parent?
|
// find parent?
|
||||||
|
// TODO do we need this if parent.tail == oldpair? The answer is no but how to organize
|
||||||
ptag = lfs_fs_parent(lfs, oldpair, &parent, false);
|
ptag = lfs_fs_parent(lfs, oldpair, &parent, false);
|
||||||
if (ptag < 0 && ptag != LFS_ERR_NOENT) {
|
if (ptag < 0 && ptag != LFS_ERR_NOENT) {
|
||||||
return ptag;
|
return ptag;
|
||||||
@@ -1880,17 +1900,56 @@ relocate:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Have parent? Didn't give up? This is when we need to reinsert
|
// Have parent? Didn't give up? This is when we need to reinsert
|
||||||
// ourself in the threaded linked-list. This evenutally creates an
|
// ourself in the threaded linked-list. This eventually creates an
|
||||||
// orphan, but we can clean that up. We need to reinsert to avoid
|
// orphan, but we can clean that up. We need to reinsert to avoid
|
||||||
// issues with cycles in non-DAG trees.
|
// issues with cycles in non-DAG trees.
|
||||||
//
|
//
|
||||||
// Note if parent's tail == us we can, and must, clean ourselves up
|
// Note if parent's tail == us we can, and must, clean ourselves up
|
||||||
// without an orphan.
|
// without an orphan.
|
||||||
if (ptag != LFS_ERR_NOENT &&
|
if (ptag != LFS_ERR_NOENT &&
|
||||||
lfs_pair_cmp(parent.tail, oldpair) != 0) {
|
(lfs_pair_cmp(parent.tail, oldpair) != 0 || !lfs_pair_sync(parent.tail, oldpair))) { // TODO word this so much better
|
||||||
|
if (lfs_pair_isnull(lfs->relocate_tail)) {
|
||||||
|
// not relocating yet
|
||||||
dir->tail[0] = parent.tail[0];
|
dir->tail[0] = parent.tail[0];
|
||||||
dir->tail[1] = parent.tail[1];
|
dir->tail[1] = parent.tail[1];
|
||||||
printf("tail? %x %x\n", dir->tail[0], dir->tail[1]);
|
} else {
|
||||||
|
printf("HEEEEEEEEEEEEY\n");
|
||||||
|
// already relocating, we need to update the last dir in our
|
||||||
|
// tail of new pairs
|
||||||
|
lfs_mdir_t relocatedir;
|
||||||
|
err = lfs_dir_fetch(lfs, &relocatedir, lfs->relocate_end);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LFS_ASSERT(!lfs->relocate_do_hack);
|
||||||
|
bool oldhack = lfs->relocate_do_hack;
|
||||||
|
lfs->relocate_do_hack = true;
|
||||||
|
err = lfs_dir_commit(lfs, &relocatedir, LFS_MKATTRS(
|
||||||
|
{LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), parent.tail}));
|
||||||
|
lfs->relocate_do_hack = oldhack;
|
||||||
|
if (err) {
|
||||||
|
// TODO do we need to clean up anything?
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir->tail[0] = lfs->relocate_tail[0];
|
||||||
|
dir->tail[1] = lfs->relocate_tail[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // TODO implement this
|
||||||
|
// if (!lfs_pair_isnull(lfs->relocate_tail)) {
|
||||||
|
// printf("HEEEEEY\n");
|
||||||
|
// }
|
||||||
|
// //LFS_ASSERT(lfs->relocate_len == 0);
|
||||||
|
// // TODO AH BUT WHAT IF WE'RE RELOCATING ALREADY
|
||||||
|
// dir->tail[0] = parent.tail[0];
|
||||||
|
// dir->tail[1] = parent.tail[1];
|
||||||
|
// printf("tail? %x %x\n", dir->tail[0], dir->tail[1]);
|
||||||
|
// lfs->relocate_tail[0] = dir->pair[0];
|
||||||
|
// lfs->relocate_tail[1] = dir->pair[1];
|
||||||
}
|
}
|
||||||
#elif 0 // TODO rm me
|
#elif 0 // TODO rm me
|
||||||
// find parent?
|
// find parent?
|
||||||
@@ -1941,6 +2000,21 @@ relocate:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (relocated) {
|
if (relocated) {
|
||||||
|
// TODO hm
|
||||||
|
if (lfs_pair_isnull(lfs->relocate_tail)) {
|
||||||
|
// TODO do this before?
|
||||||
|
lfs->relocate_end[0] = dir->pair[0];
|
||||||
|
lfs->relocate_end[1] = dir->pair[1];
|
||||||
|
}
|
||||||
|
lfs->relocate_tail[0] = dir->pair[0];
|
||||||
|
lfs->relocate_tail[1] = dir->pair[1];
|
||||||
|
|
||||||
|
// if (!dir->first) {
|
||||||
|
// // TODO is this the best way to force dir updates to be on one block?
|
||||||
|
// dir->pair[0] = dir->pair[0];
|
||||||
|
// dir->pair[1] = oldpair[1];
|
||||||
|
// }
|
||||||
|
//
|
||||||
// update references if we relocated
|
// update references if we relocated
|
||||||
LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32,
|
LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32,
|
||||||
oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
|
oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
|
||||||
@@ -1948,6 +2022,17 @@ relocate:
|
|||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO well this is the hackiest thing I've done in a while,
|
||||||
|
// needed because we may be changed during relocate (of course!)
|
||||||
|
//
|
||||||
|
// TODO we should be inserted into mlist
|
||||||
|
// TODO should mlist be reworked?
|
||||||
|
printf("refetch {%#x, %#x}\n", dir->pair[0], dir->pair[1]);
|
||||||
|
err = lfs_dir_fetch(lfs, dir, dir->pair);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2135,6 +2220,12 @@ compact:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printf("dir before: {%#x, %#x}, mlist before: ", olddir.pair[0], olddir.pair[1]);
|
||||||
|
// for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
|
// printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]);
|
||||||
|
// }
|
||||||
|
// printf("\n");
|
||||||
|
|
||||||
// this complicated bit of logic is for fixing up any active
|
// this complicated bit of logic is for fixing up any active
|
||||||
// metadata-pairs that we may have affected
|
// metadata-pairs that we may have affected
|
||||||
//
|
//
|
||||||
@@ -2143,7 +2234,9 @@ compact:
|
|||||||
// we need to copy the pair so they don't get clobbered if we refetch
|
// we need to copy the pair so they don't get clobbered if we refetch
|
||||||
// our mdir.
|
// our mdir.
|
||||||
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
if (&d->m != dir && lfs_pair_cmp(d->m.pair, olddir.pair) == 0) {
|
if (&d->m != dir && (lfs_pair_realcmp(d->m.pair, olddir.pair) == 0 ||
|
||||||
|
lfs_pair_realcmp(d->m.pair, dir->pair) == 0)) { // TODO hmm, this was updated in lfs_fs_relocate? is this double work? what do we do with olddir??
|
||||||
|
//printf("hey {%#x, %#x} -> {%#x, %#x}\n", d->m.pair[0], d->m.pair[1], dir->pair[0], dir->pair[1]);
|
||||||
d->m = *dir;
|
d->m = *dir;
|
||||||
for (int i = 0; i < attrcount; i++) {
|
for (int i = 0; i < attrcount; i++) {
|
||||||
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
||||||
@@ -2168,7 +2261,8 @@ compact:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
if (lfs_pair_cmp(d->m.pair, olddir.pair) == 0) {
|
if (lfs_pair_realcmp(d->m.pair, olddir.pair) == 0 ||
|
||||||
|
lfs_pair_realcmp(d->m.pair, dir->pair) == 0) { // TODO hm, see above
|
||||||
while (d->id >= d->m.count && !lfs_pair_isnull(d->m.branch)) {
|
while (d->id >= d->m.count && !lfs_pair_isnull(d->m.branch)) {
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
@@ -2202,6 +2296,18 @@ compact:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printf("dir after: {%#x, %#x}, mlist after: ", dir->pair[0], dir->pair[1]);
|
||||||
|
// for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
|
// printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]);
|
||||||
|
// }
|
||||||
|
// printf("\n");
|
||||||
|
|
||||||
|
if (!lfs->relocate_do_hack) {
|
||||||
|
lfs->relocate_tail[0] = LFS_BLOCK_NULL;
|
||||||
|
lfs->relocate_tail[1] = LFS_BLOCK_NULL;
|
||||||
|
lfs->relocate_end[0] = LFS_BLOCK_NULL; // TODO need these?
|
||||||
|
lfs->relocate_end[1] = LFS_BLOCK_NULL;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2293,6 +2399,10 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
|
|||||||
lfs_pair_tole32(bpair);
|
lfs_pair_tole32(bpair);
|
||||||
lfs_pair_tole32(dir.pair);
|
lfs_pair_tole32(dir.pair);
|
||||||
// TODO need to change endianness of tail?
|
// TODO need to change endianness of tail?
|
||||||
|
lfs->relocate_tail[0] = dir.pair[0]; // TODO
|
||||||
|
lfs->relocate_tail[1] = dir.pair[1];
|
||||||
|
lfs->relocate_end[0] = dir.pair[0]; // TODO
|
||||||
|
lfs->relocate_end[1] = dir.pair[1];
|
||||||
err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS(
|
err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS(
|
||||||
{LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL},
|
{LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL},
|
||||||
{LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path},
|
{LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path},
|
||||||
@@ -3683,6 +3793,15 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
|
|||||||
// let commit clean up after move (if we're different! otherwise move
|
// let commit clean up after move (if we're different! otherwise move
|
||||||
// logic already fixed it for us)
|
// logic already fixed it for us)
|
||||||
if (!samepair && lfs_gstate_hasmove(&lfs->gstate)) {
|
if (!samepair && lfs_gstate_hasmove(&lfs->gstate)) {
|
||||||
|
// fetch again
|
||||||
|
// TODO should this be in mlist?
|
||||||
|
err = lfs_dir_fetch(lfs, &oldcwd, oldcwd.pair);
|
||||||
|
if (err) {
|
||||||
|
lfs->mlist = prevdir.next;
|
||||||
|
LFS_TRACE("lfs_rename -> %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// prep gstate and delete move id
|
// prep gstate and delete move id
|
||||||
lfs_fs_prepmove(lfs, 0x3ff, NULL);
|
lfs_fs_prepmove(lfs, 0x3ff, NULL);
|
||||||
err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS(
|
err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS(
|
||||||
@@ -3893,6 +4012,11 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
|
|||||||
// setup default state
|
// setup default state
|
||||||
lfs->root[0] = LFS_BLOCK_NULL;
|
lfs->root[0] = LFS_BLOCK_NULL;
|
||||||
lfs->root[1] = LFS_BLOCK_NULL;
|
lfs->root[1] = LFS_BLOCK_NULL;
|
||||||
|
lfs->relocate_tail[0] = LFS_BLOCK_NULL;
|
||||||
|
lfs->relocate_tail[1] = LFS_BLOCK_NULL;
|
||||||
|
lfs->relocate_end[0] = LFS_BLOCK_NULL;
|
||||||
|
lfs->relocate_end[1] = LFS_BLOCK_NULL;
|
||||||
|
lfs->relocate_do_hack = false;
|
||||||
lfs->mlist = NULL;
|
lfs->mlist = NULL;
|
||||||
lfs->seed = 0;
|
lfs->seed = 0;
|
||||||
lfs->gdisk = (lfs_gstate_t){0};
|
lfs->gdisk = (lfs_gstate_t){0};
|
||||||
@@ -4270,10 +4394,33 @@ int lfs_fs_traverse(lfs_t *lfs,
|
|||||||
|
|
||||||
static int lfs_fs_pred(lfs_t *lfs,
|
static int lfs_fs_pred(lfs_t *lfs,
|
||||||
const lfs_block_t pair[2], lfs_mdir_t *pdir) {
|
const lfs_block_t pair[2], lfs_mdir_t *pdir) {
|
||||||
|
// iterate over our relocation chain
|
||||||
|
// TODO combine these loops?
|
||||||
|
pdir->tail[0] = lfs->relocate_tail[0];
|
||||||
|
pdir->tail[1] = lfs->relocate_tail[1];
|
||||||
|
lfs_block_t cycle = 0;
|
||||||
|
while (!lfs_pair_isnull(pdir->tail) &&
|
||||||
|
lfs_pair_cmp(pdir->tail, lfs->relocate_end) != 0) {
|
||||||
|
if (cycle >= lfs->cfg->block_count/2) {
|
||||||
|
// loop detected
|
||||||
|
return LFS_ERR_CORRUPT;
|
||||||
|
}
|
||||||
|
cycle += 1;
|
||||||
|
|
||||||
|
if (lfs_pair_cmp(pdir->tail, pair) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = lfs_dir_fetch(lfs, pdir, pdir->tail);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// iterate over all directory directory entries
|
// iterate over all directory directory entries
|
||||||
pdir->tail[0] = 0;
|
pdir->tail[0] = 0;
|
||||||
pdir->tail[1] = 1;
|
pdir->tail[1] = 1;
|
||||||
lfs_block_t cycle = 0;
|
cycle = 0;
|
||||||
while (!lfs_pair_isnull(pdir->tail)) {
|
while (!lfs_pair_isnull(pdir->tail)) {
|
||||||
if (cycle >= lfs->cfg->block_count/2) {
|
if (cycle >= lfs->cfg->block_count/2) {
|
||||||
// loop detected
|
// loop detected
|
||||||
@@ -4357,6 +4504,12 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
|
|||||||
static int lfs_fs_relocate(lfs_t *lfs,
|
static int lfs_fs_relocate(lfs_t *lfs,
|
||||||
lfs_stag_t ptag, lfs_mdir_t *parent,
|
lfs_stag_t ptag, lfs_mdir_t *parent,
|
||||||
const lfs_block_t oldpair[2], lfs_block_t newpair[2]) {
|
const lfs_block_t oldpair[2], lfs_block_t newpair[2]) {
|
||||||
|
printf("mlist before: ");
|
||||||
|
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
|
printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
// update internal root
|
// update internal root
|
||||||
if (lfs_pair_cmp(oldpair, lfs->root) == 0) {
|
if (lfs_pair_cmp(oldpair, lfs->root) == 0) {
|
||||||
LFS_DEBUG("Relocating root %"PRIx32" %"PRIx32,
|
LFS_DEBUG("Relocating root %"PRIx32" %"PRIx32,
|
||||||
@@ -4365,6 +4518,18 @@ static int lfs_fs_relocate(lfs_t *lfs,
|
|||||||
lfs->root[1] = newpair[1];
|
lfs->root[1] = newpair[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update relocate chain if needed
|
||||||
|
if (lfs_pair_cmp(oldpair, lfs->relocate_tail) == 0) {
|
||||||
|
lfs->relocate_tail[0] = newpair[0];
|
||||||
|
lfs->relocate_tail[1] = newpair[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lfs_pair_cmp(oldpair, lfs->relocate_end) == 0) {
|
||||||
|
lfs->relocate_end[0] = newpair[0];
|
||||||
|
lfs->relocate_end[1] = newpair[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO is this doing double work????
|
||||||
// update internally tracked dirs
|
// update internally tracked dirs
|
||||||
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
if (lfs_pair_cmp(oldpair, d->m.pair) == 0) {
|
if (lfs_pair_cmp(oldpair, d->m.pair) == 0) {
|
||||||
@@ -4379,6 +4544,12 @@ static int lfs_fs_relocate(lfs_t *lfs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("mlist after: ");
|
||||||
|
for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
|
||||||
|
printf("{%#x, %#x} ", d->m.pair[0], d->m.pair[1]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
bool parentispred = (lfs_pair_cmp(parent->tail, oldpair) == 0);
|
bool parentispred = (lfs_pair_cmp(parent->tail, oldpair) == 0);
|
||||||
|
|
||||||
// update parent if needed
|
// update parent if needed
|
||||||
@@ -4413,42 +4584,52 @@ static int lfs_fs_relocate(lfs_t *lfs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// next step, clean up orphans
|
// next step, clean up orphans
|
||||||
lfs_fs_preporphans(lfs, -!parentispred);
|
// TODO hm
|
||||||
|
//lfs_fs_preporphans(lfs, -!parentispred);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parentispred) {
|
// TODO clean this up?
|
||||||
// find pred
|
if (lfs_gstate_hasorphans(&lfs->gstate)) {
|
||||||
int err = lfs_fs_pred(lfs, oldpair, parent);
|
int err = lfs_fs_deorphan(lfs);
|
||||||
if (err && err != LFS_ERR_NOENT) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we can't find dir, it must be new
|
|
||||||
if (err != LFS_ERR_NOENT) {
|
|
||||||
// fix pending move in this pair? this looks like an optimization
|
|
||||||
// but is in fact _required_ since relocating may outdate the move.
|
|
||||||
uint16_t moveid = 0x3ff;
|
|
||||||
if (lfs_gstate_hasmovehere(&lfs->gstate, parent->pair)) {
|
|
||||||
moveid = lfs_tag_id(lfs->gstate.tag);
|
|
||||||
LFS_DEBUG("Fixing move while relocating "
|
|
||||||
"%"PRIx32" %"PRIx32" %"PRIx16"\n",
|
|
||||||
parent->pair[0], parent->pair[1], moveid);
|
|
||||||
lfs_fs_prepmove(lfs, 0x3ff, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace bad pair, either we clean up desync, or no desync occured
|
|
||||||
lfs_pair_tole32(newpair);
|
|
||||||
err = lfs_dir_commit(lfs, parent, LFS_MKATTRS(
|
|
||||||
{LFS_MKTAG_IF(moveid != 0x3ff,
|
|
||||||
LFS_TYPE_DELETE, moveid, 0)},
|
|
||||||
{LFS_MKTAG(LFS_TYPE_TAIL + parent->split, 0x3ff, 8),
|
|
||||||
newpair}));
|
|
||||||
lfs_pair_fromle32(newpair);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
//
|
||||||
|
// if (!parentispred) {
|
||||||
|
// // find pred
|
||||||
|
// int err = lfs_fs_pred(lfs, oldpair, parent);
|
||||||
|
// if (err && err != LFS_ERR_NOENT) {
|
||||||
|
// return err;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // if we can't find dir, it must be new
|
||||||
|
// if (err != LFS_ERR_NOENT) {
|
||||||
|
// // fix pending move in this pair? this looks like an optimization
|
||||||
|
// // but is in fact _required_ since relocating may outdate the move.
|
||||||
|
// uint16_t moveid = 0x3ff;
|
||||||
|
// if (lfs_gstate_hasmovehere(&lfs->gstate, parent->pair)) {
|
||||||
|
// moveid = lfs_tag_id(lfs->gstate.tag);
|
||||||
|
// LFS_DEBUG("Fixing move while relocating "
|
||||||
|
// "%"PRIx32" %"PRIx32" %"PRIx16"\n",
|
||||||
|
// parent->pair[0], parent->pair[1], moveid);
|
||||||
|
// lfs_fs_prepmove(lfs, 0x3ff, NULL);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // replace bad pair, either we clean up desync, or no desync occured
|
||||||
|
// lfs_pair_tole32(newpair);
|
||||||
|
// err = lfs_dir_commit(lfs, parent, LFS_MKATTRS(
|
||||||
|
// {LFS_MKTAG_IF(moveid != 0x3ff,
|
||||||
|
// LFS_TYPE_DELETE, moveid, 0)},
|
||||||
|
// {LFS_MKTAG(LFS_TYPE_TAIL + parent->split, 0x3ff, 8),
|
||||||
|
// newpair}));
|
||||||
|
// lfs_pair_fromle32(newpair);
|
||||||
|
// if (err) {
|
||||||
|
// return err;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -4503,12 +4684,16 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO needme?
|
||||||
|
LFS_ASSERT(lfs_pair_isnull(lfs->relocate_tail));
|
||||||
|
|
||||||
// Fix any orphans
|
// Fix any orphans
|
||||||
lfs_mdir_t pdir = {.split = true, .tail = {0, 1}};
|
lfs_mdir_t pdir = {.split = true, .tail = {0, 1}};
|
||||||
lfs_mdir_t dir;
|
lfs_mdir_t dir;
|
||||||
|
|
||||||
// iterate over all directory directory entries
|
// iterate over all directory directory entries
|
||||||
while (!lfs_pair_isnull(pdir.tail)) {
|
// TODO handle this more gracefully
|
||||||
|
while (lfs_gstate_hasorphans(&lfs->gstate) && !lfs_pair_isnull(pdir.tail)) {
|
||||||
int err = lfs_dir_fetch(lfs, &dir, pdir.tail);
|
int err = lfs_dir_fetch(lfs, &dir, pdir.tail);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
|
|||||||
7
lfs.h
7
lfs.h
@@ -117,7 +117,8 @@ enum lfs_type {
|
|||||||
// internal chip sources
|
// internal chip sources
|
||||||
LFS_FROM_NOOP = 0x000,
|
LFS_FROM_NOOP = 0x000,
|
||||||
LFS_FROM_MOVE = 0x101,
|
LFS_FROM_MOVE = 0x101,
|
||||||
LFS_FROM_USERATTRS = 0x102,
|
LFS_FROM_DROP = 0x102,
|
||||||
|
LFS_FROM_USERATTRS = 0x103,
|
||||||
};
|
};
|
||||||
|
|
||||||
// File open flags
|
// File open flags
|
||||||
@@ -311,6 +312,7 @@ typedef struct lfs_mdir {
|
|||||||
uint32_t etag;
|
uint32_t etag;
|
||||||
uint16_t count;
|
uint16_t count;
|
||||||
bool erased;
|
bool erased;
|
||||||
|
bool first; // TODO come on
|
||||||
bool split;
|
bool split;
|
||||||
lfs_block_t tail[2];
|
lfs_block_t tail[2];
|
||||||
lfs_block_t branch[2];
|
lfs_block_t branch[2];
|
||||||
@@ -368,6 +370,9 @@ typedef struct lfs {
|
|||||||
lfs_cache_t pcache;
|
lfs_cache_t pcache;
|
||||||
|
|
||||||
lfs_block_t root[2];
|
lfs_block_t root[2];
|
||||||
|
lfs_block_t relocate_tail[2];
|
||||||
|
lfs_block_t relocate_end[2];
|
||||||
|
bool relocate_do_hack; // TODO fixme
|
||||||
struct lfs_mlist {
|
struct lfs_mlist {
|
||||||
struct lfs_mlist *next;
|
struct lfs_mlist *next;
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ def main(args):
|
|||||||
mdir.rev,
|
mdir.rev,
|
||||||
' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:])
|
' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:])
|
||||||
if len(mdir.pair) > 1 else '',
|
if len(mdir.pair) > 1 else '',
|
||||||
' (corrupted)' if not mdir else ''))
|
' (corrupted!)' if not mdir else ''))
|
||||||
if args.all:
|
if args.all:
|
||||||
mdir.dump_all(truncate=not args.no_truncate)
|
mdir.dump_all(truncate=not args.no_truncate)
|
||||||
elif args.log:
|
elif args.log:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import sys
|
|||||||
import json
|
import json
|
||||||
import io
|
import io
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
import collections as c
|
||||||
from readmdir import Tag, MetadataPair
|
from readmdir import Tag, MetadataPair
|
||||||
|
|
||||||
def popc(x):
|
def popc(x):
|
||||||
@@ -13,7 +14,7 @@ def popc(x):
|
|||||||
def ctz(x):
|
def ctz(x):
|
||||||
return len(bin(x)) - len(bin(x).rstrip('0'))
|
return len(bin(x)) - len(bin(x).rstrip('0'))
|
||||||
|
|
||||||
def dumpentries(args, mdir, f):
|
def dumpentries(args, mdir, mdirs, f):
|
||||||
for k, id_ in enumerate(mdir.ids):
|
for k, id_ in enumerate(mdir.ids):
|
||||||
name = mdir[Tag('name', id_, 0)]
|
name = mdir[Tag('name', id_, 0)]
|
||||||
struct_ = mdir[Tag('struct', id_, 0)]
|
struct_ = mdir[Tag('struct', id_, 0)]
|
||||||
@@ -22,8 +23,10 @@ def dumpentries(args, mdir, f):
|
|||||||
id_, name.typerepr(),
|
id_, name.typerepr(),
|
||||||
json.dumps(name.data.decode('utf8')))
|
json.dumps(name.data.decode('utf8')))
|
||||||
if struct_.is_('dirstruct'):
|
if struct_.is_('dirstruct'):
|
||||||
desc += " dir {%#x, %#x}" % struct.unpack(
|
pair = struct.unpack('<II', struct_.data[:8].ljust(8, b'\xff'))
|
||||||
'<II', struct_.data[:8].ljust(8, b'\xff'))
|
desc += " dir {%#x, %#x}%s" % (
|
||||||
|
pair[0], pair[1],
|
||||||
|
'?' if frozenset(pair) not in mdirs else '')
|
||||||
if struct_.is_('ctzstruct'):
|
if struct_.is_('ctzstruct'):
|
||||||
desc += " ctz {%#x} size %d" % struct.unpack(
|
desc += " ctz {%#x} size %d" % struct.unpack(
|
||||||
'<II', struct_.data[:8].ljust(8, b'\xff'))
|
'<II', struct_.data[:8].ljust(8, b'\xff'))
|
||||||
@@ -93,19 +96,15 @@ def dumpentries(args, mdir, f):
|
|||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
with open(args.disk, 'rb') as f:
|
with open(args.disk, 'rb') as f:
|
||||||
dirs = []
|
|
||||||
superblock = None
|
superblock = None
|
||||||
gstate = b''
|
gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0'
|
||||||
mdirs = []
|
mdirs = c.OrderedDict()
|
||||||
cycle = False
|
cycle = False
|
||||||
tail = (args.block1, args.block2)
|
tail = (args.block1, args.block2)
|
||||||
hard = False
|
while tail:
|
||||||
while True:
|
if frozenset(tail) in mdirs:
|
||||||
for m in it.chain((m for d in dirs for m in d), mdirs):
|
|
||||||
if set(m.blocks) == set(tail):
|
|
||||||
# cycle detected
|
# cycle detected
|
||||||
cycle = m.blocks
|
cycle = tail
|
||||||
if cycle:
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# load mdir
|
# load mdir
|
||||||
@@ -128,6 +127,13 @@ def main(args):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
mdir.tail = None
|
mdir.tail = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
mdir.branch = mdir[Tag('branch', 0, 0)]
|
||||||
|
if mdir.branch.size != 8 or mdir.branch.data == 8*b'\xff':
|
||||||
|
mdir.branch = None
|
||||||
|
except KeyError:
|
||||||
|
mdir.branch = None
|
||||||
|
|
||||||
# have superblock?
|
# have superblock?
|
||||||
try:
|
try:
|
||||||
nsuperblock = mdir[
|
nsuperblock = mdir[
|
||||||
@@ -144,41 +150,55 @@ def main(args):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# add to directories
|
# add to metadata-pairs
|
||||||
mdirs.append(mdir)
|
mdirs[frozenset(mdir.blocks)] = mdir
|
||||||
if mdir.tail is None or not mdir.tail.is_('hardtail'):
|
tail = (struct.unpack('<II', mdir.tail.data)
|
||||||
dirs.append(mdirs)
|
if mdir.tail else None)
|
||||||
mdirs = []
|
|
||||||
|
|
||||||
if mdir.tail is None:
|
# derive paths and build directories
|
||||||
break
|
dirs = {}
|
||||||
|
rogue = {}
|
||||||
tail = struct.unpack('<II', mdir.tail.data)
|
pending = [('/', (args.block1, args.block2))]
|
||||||
hard = mdir.tail.is_('hardtail')
|
|
||||||
|
|
||||||
# find paths
|
|
||||||
dirtable = {}
|
|
||||||
for dir in dirs:
|
|
||||||
dirtable[frozenset(dir[0].blocks)] = dir
|
|
||||||
|
|
||||||
pending = [("/", dirs[0])]
|
|
||||||
while pending:
|
while pending:
|
||||||
path, dir = pending.pop(0)
|
path, branch = pending.pop(0)
|
||||||
for mdir in dir:
|
dir = []
|
||||||
|
while branch and frozenset(branch) in mdirs:
|
||||||
|
mdir = mdirs[frozenset(branch)]
|
||||||
|
dir.append(mdir)
|
||||||
|
|
||||||
for tag in mdir.tags:
|
for tag in mdir.tags:
|
||||||
if tag.is_('dir'):
|
if tag.is_('dir'):
|
||||||
try:
|
try:
|
||||||
npath = tag.data.decode('utf8')
|
npath = path + '/' + tag.data.decode('utf8')
|
||||||
|
npath = npath.replace('//', '/')
|
||||||
dirstruct = mdir[Tag('dirstruct', tag.id, 0)]
|
dirstruct = mdir[Tag('dirstruct', tag.id, 0)]
|
||||||
nblocks = struct.unpack('<II', dirstruct.data)
|
npair = struct.unpack('<II', dirstruct.data)
|
||||||
nmdir = dirtable[frozenset(nblocks)]
|
pending.append((npath, npair))
|
||||||
pending.append(((path + '/' + npath), nmdir))
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
dir[0].path = path.replace('//', '/')
|
branch = (struct.unpack('<II', mdir.branch.data)
|
||||||
|
if mdir.branch else None)
|
||||||
|
|
||||||
# dump tree
|
if not dir:
|
||||||
|
rogue[path] = branch
|
||||||
|
else:
|
||||||
|
dirs[path] = dir
|
||||||
|
|
||||||
|
# also find orphans
|
||||||
|
not_orphans = {frozenset(mdir.blocks)
|
||||||
|
for dir in dirs.values()
|
||||||
|
for mdir in dir}
|
||||||
|
orphans = []
|
||||||
|
for pair, mdir in mdirs.items():
|
||||||
|
if pair not in not_orphans:
|
||||||
|
if len(orphans) > 0 and (pair == frozenset(
|
||||||
|
struct.unpack('<II', orphans[-1][-1].tail.data))):
|
||||||
|
orphans[-1].append(mdir)
|
||||||
|
else:
|
||||||
|
orphans.append([mdir])
|
||||||
|
|
||||||
|
# print littlefs + version info
|
||||||
version = ('?', '?')
|
version = ('?', '?')
|
||||||
if superblock:
|
if superblock:
|
||||||
version = tuple(reversed(
|
version = tuple(reversed(
|
||||||
@@ -187,24 +207,30 @@ def main(args):
|
|||||||
"data (truncated, if it fits)"
|
"data (truncated, if it fits)"
|
||||||
if not any([args.no_truncate, args.tags, args.log, args.all]) else ""))
|
if not any([args.no_truncate, args.tags, args.log, args.all]) else ""))
|
||||||
|
|
||||||
if gstate:
|
# print gstate
|
||||||
print("gstate 0x%s" % ''.join('%02x' % c for c in gstate))
|
print("gstate 0x%s" % ''.join('%02x' % c for c in gstate))
|
||||||
tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0])
|
tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0])
|
||||||
blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff'))
|
blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff'))
|
||||||
if tag.size or not tag.isvalid:
|
if tag.size or not tag.isvalid:
|
||||||
print(" orphans >=%d" % max(tag.size, 1))
|
print(" orphans >=%d" % max(tag.size, 1))
|
||||||
if tag.type:
|
if tag.type:
|
||||||
print(" move dir {%#x, %#x} id %d" % (
|
print(" move dir {%#x, %#x}%s id %d" % (
|
||||||
blocks[0], blocks[1], tag.id))
|
blocks[0], blocks[1],
|
||||||
|
'?' if frozenset(blocks) not in mdirs else '',
|
||||||
|
tag.id))
|
||||||
|
|
||||||
for i, dir in enumerate(dirs):
|
# print dir info
|
||||||
print("dir %s" % (json.dumps(dir[0].path)
|
for path, dir in it.chain(
|
||||||
if hasattr(dir[0], 'path') else '(orphan)'))
|
sorted(dirs.items()),
|
||||||
|
zip(it.repeat(None), orphans)):
|
||||||
|
print("dir %s" % json.dumps(path) if path else "orphaned")
|
||||||
|
|
||||||
for j, mdir in enumerate(dir):
|
for j, mdir in enumerate(dir):
|
||||||
print("mdir {%#x, %#x} rev %d%s" % (
|
print("mdir {%#x, %#x} rev %d (was %d)%s%s" % (
|
||||||
mdir.blocks[0], mdir.blocks[1], mdir.rev,
|
mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev,
|
||||||
' (corrupted)' if not mdir else ''))
|
' (corrupted!)' if not mdir else '',
|
||||||
|
' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data)
|
||||||
|
if mdir.tail else ''))
|
||||||
|
|
||||||
f = io.StringIO()
|
f = io.StringIO()
|
||||||
if args.tags:
|
if args.tags:
|
||||||
@@ -214,21 +240,27 @@ def main(args):
|
|||||||
elif args.all:
|
elif args.all:
|
||||||
mdir.dump_all(f, truncate=not args.no_truncate)
|
mdir.dump_all(f, truncate=not args.no_truncate)
|
||||||
else:
|
else:
|
||||||
dumpentries(args, mdir, f)
|
dumpentries(args, mdir, mdirs, f)
|
||||||
|
|
||||||
lines = list(filter(None, f.getvalue().split('\n')))
|
lines = list(filter(None, f.getvalue().split('\n')))
|
||||||
for k, line in enumerate(lines):
|
for k, line in enumerate(lines):
|
||||||
print("%s %s" % (
|
print("%s %s" % (
|
||||||
' ' if i == len(dirs)-1 and j == len(dir)-1 else
|
' ' if j == len(dir)-1 else
|
||||||
'v' if k == len(lines)-1 else
|
'v' if k == len(lines)-1 else
|
||||||
'.' if j == len(dir)-1 else
|
'|' if path else '.',
|
||||||
'|',
|
|
||||||
line))
|
line))
|
||||||
|
|
||||||
if cycle:
|
for path, pair in rogue.items():
|
||||||
print("*** cycle detected! -> {%#x, %#x} ***" % (cycle[0], cycle[1]))
|
print("*** couldn't find dir %s {%#x, %#x}! ***" % (
|
||||||
|
json.dumps(path), pair[0], pair[1]))
|
||||||
|
|
||||||
if cycle:
|
if cycle:
|
||||||
|
print("*** cycle detected {%#x, %#x}! ***" % (
|
||||||
|
cycle[0], cycle[1]))
|
||||||
|
|
||||||
|
if cycle:
|
||||||
|
return 3
|
||||||
|
elif rogue:
|
||||||
return 2
|
return 2
|
||||||
elif not all(mdir for dir in dirs for mdir in dir):
|
elif not all(mdir for dir in dirs for mdir in dir):
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ class TestCase:
|
|||||||
ncmd.extend(['-ex', 'r'])
|
ncmd.extend(['-ex', 'r'])
|
||||||
if failure.assert_:
|
if failure.assert_:
|
||||||
ncmd.extend(['-ex', 'up 2'])
|
ncmd.extend(['-ex', 'up 2'])
|
||||||
elif gdb == 'start':
|
elif gdb == 'start' or isinstance(gdb, int):
|
||||||
ncmd.extend([
|
ncmd.extend([
|
||||||
'-ex', 'b %s:%d' % (self.suite.path, self.code_lineno),
|
'-ex', 'b %s:%d' % (self.suite.path, self.code_lineno),
|
||||||
'-ex', 'r'])
|
'-ex', 'r'])
|
||||||
@@ -329,7 +329,9 @@ class ReentrantTestCase(TestCase):
|
|||||||
persist = 'noerase'
|
persist = 'noerase'
|
||||||
|
|
||||||
# exact cycle we should drop into debugger?
|
# exact cycle we should drop into debugger?
|
||||||
if gdb and failure and failure.cycleno == cycles:
|
if gdb and failure and (
|
||||||
|
failure.cycleno == cycles or
|
||||||
|
(isinstance(gdb, int) and gdb == cycles)):
|
||||||
return super().test(gdb=gdb, persist=persist, cycles=cycles,
|
return super().test(gdb=gdb, persist=persist, cycles=cycles,
|
||||||
failure=failure, **args)
|
failure=failure, **args)
|
||||||
|
|
||||||
@@ -760,7 +762,8 @@ if __name__ == "__main__":
|
|||||||
help="Store disk image in a file.")
|
help="Store disk image in a file.")
|
||||||
parser.add_argument('-b', '--build', action='store_true',
|
parser.add_argument('-b', '--build', action='store_true',
|
||||||
help="Only build the tests, do not execute.")
|
help="Only build the tests, do not execute.")
|
||||||
parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'],
|
parser.add_argument('-g', '--gdb', metavar='{init,start,assert},CYCLE',
|
||||||
|
type=lambda n: n if n in {'init', 'start', 'assert'} else int(n, 0),
|
||||||
nargs='?', const='assert',
|
nargs='?', const='assert',
|
||||||
help="Drop into gdb on test failure.")
|
help="Drop into gdb on test failure.")
|
||||||
parser.add_argument('--no-internal', action='store_true',
|
parser.add_argument('--no-internal', action='store_true',
|
||||||
|
|||||||
Reference in New Issue
Block a user